Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to restrict a method only to being used with a specific type?

Tags:

scala

Suppose I have a case class below

case class SomeCaseClass[M] private (
  value: String
) 

and in another file, I have the following trait and object.

trait SomeTrait[A] {
  def get(oldId: String): A
   :
}

object SomeObject {
  private[this] def init[A](): SomeTrait[A] = new SomeTrait[A] {
    def get(oldId: String): A = id(oldId)
     :
  }

  val aaa: SomeTrait[String] = init[String]()
  val bbb: SomeTrait[SomeCaseClass[String]] = init[SomeCaseClass[String]]()
}

How should I modify the code so that restrict the init method only to being used with SomeCaseClass[_] type and not with any types like String as above?

Ideally with some modification to the code, the line val aaa: SomeTrait[String] = init[String]() should cause compilation error.

like image 596
d-_-b Avatar asked May 02 '19 10:05

d-_-b


People also ask

Can a method be private in Java?

The Java language supports five distinct access levels for methods: private, private protected, protected, public, and, if left unspecified, "friendly".

How do you restrict a class from being instantiated more than once?

Create a static factory method that keeps track of the instances created. With a private constructor, the users have to use the factory method, which can then throw the exception if more than 5 objects have already been created. One could do the same with an ordinary constructor and some static instance counter.

How do you stop a method from calling in Java?

JButton stopCaptureButton = new JButton("Stop"); panel. add(stopCaptureButton); stopCaptureButton. setBounds(875, 350, 80, 30); stopCaptureButton. addActionListener(new ActionListener(){ @Override public void actionPerformed(ActionEvent e){ EspduReceiver.


2 Answers

This is what I came up with:

case class SomeCaseClass[M] private (
  value: String
) 

trait SomeTrait[A] {
  def get(oldId: String): A
}

private[this] def init[A <: SomeCaseClass[_]](): SomeTrait[A] = new SomeTrait[A] {
  def get(oldId: String): A = ???
}

val aaa: SomeTrait[String] = init[String]() // Will fail
val bbb: SomeTrait[SomeCaseClass[String]] = init[SomeCaseClass[String]]()

It fails with

ScalaFiddle.scala:16: error: type arguments [String] do not conform to method init's type parameter bounds [A <: ScalaFiddle.this.SomeCaseClass[_$1] forSome { type _$1 }]

You can check this scalafiddle.

I do not know if this is the best approach, but init[A <: SomeCaseClass[_]] is adding a type bound to A, and forcing A to be a Subclass of SomeCaseClass. I would love to know if there is a better way though.

like image 139
Alejandro Alcalde Avatar answered Sep 25 '22 22:09

Alejandro Alcalde


You can force a type parameter to be equal to some type B by using an implicit parameter:

def foo[A](implicit e: A =:= B): …

Also see this question.

To add some more value to this answer. Following code shows how to use the implicit parameter e: A =:= String to convert an A to a String.

def bar(b: String): Unit = println(b)
def foo[A](a: A)(implicit e: A =:= String): Unit = {
  bar(e(a))
}

foo("hi")  //compiles

foo(5)     //error: Cannot prove that scala.this.Int =:= String.

Answer to problem the OP has

This problem is much simpler: Make the method parametric only in the parameter A of SomeCaseClass[A], instead of using the whole type SomeCaseClass[A] as a type parameter:

private[this] def init[A](): SomeTrait[SomeCaseClass[A]] = new
  SomeTrait[SomeCaseClass[A]] {
    def get(oldId: String): SomeCaseClass[A] = ???
  }
like image 22
ziggystar Avatar answered Sep 22 '22 22:09

ziggystar