简体   繁体   English

如何解决scala中的“类型不匹配,预期:_ $ 1,实际:任何”错误?

[英]How to get around the error “Type mismatch, expected: _$1, actual: Any” in scala?

I have a scenario similar to the following example 我有一个类似于以下示例的方案

// Reads from somewhere and returns List of type A
trait Reader[A]  {
  def read(): List[A]

}

// Transforms element of type A, performs some operation
// and converts result to string
trait Translator[A]  {
  def translate(a: A): String
}



object Factory {

  def getReader(readerName:String) = {

    readerName match {
     case "one" =>  new Reader[String] {
        override def read() = List("sasha")
     }
     case "two" => new Reader[Int] {
        override def read() = List(3)
     }
     case _ => throw new IllegalArgumentException
   }

 }

 def getTranslator(translatorName:String) =  {

   translatorName match {
     case "one" => new Translator[String] {
        override def translate (a: String) = a + " " + "nice!!!"
     }
     case "two" => new Translator[Int] {
        override def translate(a: Int) = (a+2).toString
     }
     case "three" => new Translator[Int] {
        override def translate(a: Int) = (a+3).toString
     }
     case _ => throw new IllegalArgumentException
   }
 }

}

Now when I do Factory.getReader("one").read().map(a => Factory.getTranslator("one").translate(a)) , I get a compile time error "Type mismatch, expected: _$1, actual: Any". 现在,当我执行Factory.getReader("one").read().map(a => Factory.getTranslator("one").translate(a)) ,我得到一个编译时错误“类型不匹配,预期:_ $ 1,实际:任何”。

If I am not wrong, compiler is saying it can't tell if the type A of reader is same as type A of translator. 如果我没有记错的话,编译器会说它无法确定阅读器的A类型是否与翻译器的A类型相同。

I am unable to find online, how I can get around this problem ? 我在网上找不到,如何解决这个问题? Am I doing something fundamentally wrong in defining the classes the way I have ? 在定义我所拥有的类的过程中,我在做根本上错误的事情吗?

EDIT 编辑

Reason I have separate reader and translator is that, they seem to have different jobs, and I could apply more than one translator to the output of one reader (translator 2 and 3 above can be applied to output of reader 2). 我将阅读器和翻译器分开的原因是,它们似乎有不同的工作,我可以将一个以上的翻译器应用于一个阅读器的输出(上面的翻译器2和3可以应用于阅读器2的输出)。

The trouble with your approach is that you've defined a couple methods in which you want to return a different type depending on your input, but that input doesn't have any type information for the compiler to work with. 这种方法的麻烦之处在于,您已经定义了几个方法,根据输入您想返回不同的类型,但是该输入没有供编译器使用的任何类型信息。

Ie your getReader function needs to act as both a String => Reader[String] and a String => Reader[Int] depending on the value (not type !) of the input. 也就是说,取决于输入的 (不是type !), getReader函数需要同时充当String => Reader[String]String => Reader[Int] The compiler tries to figure out the common type between those two cases and comes up with String => Reader[Any] , but then when you try to use that, the loss of the specific type information becomes a problem. 编译器试图找出这两种情况之间的通用类型,并提出String => Reader[Any] ,但是当您尝试使用它时,特定类型信息的丢失成为一个问题。

I think the best solution to this is to introduce a type parameter on the input side of your method, eg 我认为对此的最佳解决方案是在方法的输入端引入类型参数,例如

case class ReaderName[T](name: String)

// these would be defined as constants on an object somewhere
val ReaderOne = ReaderName[String]("one")
val ReaderTwo = ReaderName[Int]("two")

def getReader[T](readerName: ReaderName[T]): Reader[T] = readerName match {
  case ReaderOne => new Reader[String] { ... }
  case ReaderTwo => new Reader[Int] { ... }
  case _ => throw ...
}

The point here is that you're attaching type information to a name in the form of the [T] argument on ReaderName . 这里的重点是您要在ReaderName上以[T]参数的形式将类型信息附加到名称上。 That type information can be used by the compiler to ensure you get a Reader instance for the appropriate T type based on the ReaderName you pass in. 编译器可以使用该类型信息来确保您根据传入的ReaderName获得相应T类型的Reader实例。

Note that you shouldn't try do this: 请注意,您不应尝试执行以下操作:

getReader(ReaderName[Int]("one")) // no!

because it will likely match ReaderOne despite having the wrong type parameter (thanks to type erasure on the JVM), and you'll run into ClassCastExceptions down the road. 因为尽管类型参数错误(由于要在JVM上进行类型擦除),它仍可能与ReaderOne匹配,并且您将在以后遇到ClassCastExceptions。 You should be defining ReaderName instances as constants, and referencing those constants. 您应该将ReaderName实例定义为常量,并引用这些常量。

A similar approach would be to define the ReaderNames as a sealed trait/abstract class; 一种类似的方法是将ReaderNames定义为密封的特征/抽象类。 doing so would allow you to avoid having to throw IllegalArgumentExceptions, and would help protect against the "no!" 这样做将使您避免不得不抛出IllegalArgumentExceptions,并有助于防止出现“否!”。 example above. 上面的例子。

sealed abstract class ReaderName[T](val name: String)
object ReaderName {
  case object One extends ReaderName[String]
  case object Two extends ReaderName[Int]
}

def getReader[T](name: ReaderName[T]): Reader[T] = readerName match {
  case ReaderName.One => new Reader[String] { ... }
  case ReaderName.Two => new Reader[Int] { ... }
  // no need for a `case _` becase ReaderName is sealed and we have handled all cases
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM