Domanda

This fails to compile using Scala 2.10

object TestImplicits {
  class View
  implicit class RichView[A <: View](v: A) {
    def onClicked() = println(v + " was clicked")
  }
  implicit def intToView[A <: View](i: Int): A = ???
  implicit def intToRichView(id: Int): RichView[View] = ???
  1.onClicked()
}

The error is the following:

<console>:20: error: type mismatch;
 found   : Int(1)
 required: ?{def onClickedd: ?}
Note that implicit conversions are not applicable because they are ambiguous:
 both method intToView in object TestImplicits of type [A <: TestImplicits.View]
(i: Int)A
 and method intToRichView in object TestImplicits of type (id: Int)TestImplicits
.RichView[TestImplicits.View]
 are possible conversion functions from Int(1) to ?{def onClickedd: ?}
                  1.onClickedd()
                  ^

But wait, the class View does not have a method onClicked ! Here is why I have problems:

  • I cannot remove the first implicit function as I use it for the implementation of my second function.
  • If I remove the second implicit function, I lose power functionality.

How can I keep both?

Update

If I change 1.onClicked() to intToView(1).onClicked(), I get the following interesting error:

<console>:20: error: value onClicked is not a member of Nothing
                  intToView(1).onClicked()
                               ^

I would be grateful if someone could explain this to me.

È stato utile?

Soluzione 2

Here is my solution to cover cases such as TextView, etc: I removed the implicit from the generic function, and explicitely added implicit functions to convert id to a view. That way, I can have all aspects I want.

object TestImplicits {
  class View
  class TextView extends View { def setText(s: String): Unit = println(s"text set! $s"}
  implicit class RichView(v: View) {
    def onClicked() = println(s"$v was clicked")
  }
  def intToView[A <: View](id: Int): A = findViewById(id).asInstanceOf[A]
  implicit def intToTextView(id: Int): TextView = intToView[TextView](id)
  implicit def intToRichView(id: Int): RichView[View] = new RichView(intToView[View](id))
  1.onClicked()
  2.setText("my text")
}

Altri suggerimenti

The real problem is that your intToView method fundamentally doesn't make sense and can't be usefully implemented.

Right now it effectively says "give me an integer and tell me a subtype of View, and I'll give you an instance of that subtype". But this isn't possible, because I can invent some pretty messed-up classes that extend View. For example:

class NamedView(val name: String) extends View

The type signature of your intToView method promises that the following should work:

val viewName: String = intToView[NamedView](13).name

What could this possibly be? How could you implement intToView in such a way that it would know how to create a NamedView, which I've just made up on the spot?

So ??? has allowed you to write a method that compiles but doesn't make sense. This is a dangerous situation to be in, since it means the compiler is liable to do all kinds of stupid things down the line.

In this case the stupid thing it does is decide that what it really has here is an implicit conversion from Int to Nothing, and of course Nothing is a subtype of RichView[_], so it also has an implicit conversion from Int to RichView[_]. Then you give it another implicit conversion from Int to RichView[_] and it chokes.

The simplest solution in this case is to remove the type parameter from intToView:

implicit def intToView(i: Int): View = ???

Whether this will work in your real use case depends on exactly what you're trying to do.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top