【问题标题】:Class type required but T found需要类类型但找到 T
【发布时间】:2024-01-04 12:12:01
【问题描述】:

由于某种原因,我无法创建演员(这是我的类层次结构的简单版本):

abstract class Class1[T <: Class2[_]: ClassTag] extends Actor {
   //....
   val res = List(1, 2, 3) map { x => context actorOf Props(new T(x)) } // error
}

abstract class Class2[U <: Class3 : ClassTag](a: Int) extends Actor { ... }
abstract class Class3(b: Int) extends Actor

但是有一个错误提示 class type required but T found

【问题讨论】:

    标签: scala akka


    【解决方案1】:

    您不能使用类型参数T 调用new T(x)T 可能没有这样的构造函数:

    class WithoutSuchConstructor extends Class2[Class3](1)
    

    你应该明确指定创建T的方法:

    abstract class Class1[T <: Class2[_]: ClassTag] extends Actor {
      //....
      def createT(i: Int): T
      val res = List(1, 2, 3) map { x => context actorOf Props(createT(x)) }
    }
    

    或者:

    abstract class Class1[T <: Class2[_]: ClassTag](createT: Int => T) extends Actor {
      //....
      val res = List(1, 2, 3) map { x => context actorOf Props(createT(x)) }
    }
    

    【讨论】:

    • 有什么方法可以指定 T 必须是 C# 中的构造函数:where T: new()?
    • @MariusKavansky,没有这样的方法。实际上,在 C# 中,它是附加 Func 参数的语法糖(就像在我的第二个代码示例中一样)。
    • 你的意思可能是abstract createT(i: Int): T中的def?
    • 当我在子类中实现def createT(i: Int): T 时(使用具体类而不是 T),它显示“类型不匹配:找到:MyConcreteClass,不需要任何内容​​”。你知道这是为什么吗?
    • @MariusKavansky:我猜你没有指定类型参数T。您能否向您展示代码示例(使用 pastebin)?
    【解决方案2】:

    我使用的一种方法是创建一个“实例化器”特征,该特征可用于创建存在隐式实例化器的类型的实例:

    trait Instantiator[+A] {
      def apply(): A
    }
    
    object Instantiator {
      def apply[A](create: => A): Instantiator[A] = new Instantiator[A] {
        def apply(): A = create
      }
    }
    
    class Foo() { ... }
    
    object Foo {
      implicit val instantiator: Instantiator[Foo] = Instantiator { new Foo() }
    }
    
    // def someMethod[A]()(implicit instantiator: Instantiator[A]): A = {
    def someMethod[A : Instantiator](): A = {
      val a = implicitly[Instantiator[A]].apply()
      ...
    }
    
    someMethod[Foo]()
    

    【讨论】:

      【解决方案3】:

      我认为这是因为 JVM 限制也称为“类型擦除”。 http://docs.oracle.com/javase/tutorial/java/generics/erasure.html

      另请参阅“无法创建类型参数的实例”,网址为 http://docs.oracle.com/javase/tutorial/java/generics/restrictions.html

      顺便说一句,C# 允许您编写:

      new T()
      

      当你定义一个限制时

      where T: new()
      

      但不幸的是构造函数必须是无参数的

      【讨论】:

      • C# 中它是一个编译时语法糖,它与类型擦除无关。
      • 是的。 C# 有真正的泛型,它们是运行时的。
      • 我的意思是在scalajava 中没有像: new() 这样的语法不是因为type erasure - 这是一个编译时机制,scala 语言开发人员可以在@ 上实现它987654331@。他们只是认为没有必要。
      • 在 scala 中你可以编写 [T: Manifest],然后使用隐式 Manifest(或较新版本的 TypeTag)通过反射创建一个 T。谷歌上下文边界进一步解释。