1

我想调用泛型类型的构造函数T,但我也希望它有一个只有一个Int参数的特定构造函数:

 class Class1[T] {
   def method1(i: Int) = {
     val instance = new T(i)  //ops!
     i
   }
 }

如何指定此要求?

更新:使用这样的东西有多可接受(灵活等)?那是一个模板方法模式。

 abstract class Class1[T] {

   def creator: Int => T

   def method1(i: Int) = {
     val instance = creator(i)  //seems ok
     i
   }
 }
4

2 回答 2

5

Scala 不允许您在类型约束(例如 C#)中指定构造函数的签名。

但是,Scala 确实允许您通过使用类型类模式来实现等效的目标。这更灵活,但需要编写更多样板代码。

首先,定义 atrait这将是一个用于创建T给定 an的接口Int

trait Factory[T] {
  def fromInt(i: Int): T
}

然后,implicit为您想要的任何类型定义一个实例。假设您有一些class Foo带有适当的构造函数。

implicit val FooFactory = new Factory[Foo] {
  def fromInt(i: Int) = new Foo(i)
}

现在,您可以为以下签名中的类型参数指定上下文绑定TClass1

class Class1[T : Factory] {
  def method1(i: Int) = {
    val instance = implicitly[Factory[T]].fromInt(i)
    // ...
  }
}

约束T : Factory说必须有一个隐含Factory[T]的范围。当您需要使用该实例时,您可以使用该implicitly方法从隐式范围中获取它。

或者,您可以将工厂指定为需要它的方法的隐式参数。

class Class1[T] {
  def method1(i: Int)(implicit factory: Factory[T]) = {
    val instance = factory.fromInt(i)
    // ...
  }
}

这比将约束放在类签名中更灵活,因为这意味着您可以Class1拥有其他不需要Factory[T]. Factory[T]在这种情况下,除非您调用需要它的方法之一,否则编译器不会强制执行它。


为了响应您的更新(使用 abstractcreator方法),这是一种完全合理的方法,只要您不介意为 each 创建一个子Class1类型T。另请注意,T在您要创建 的实例的任何时候,都需要是具体类型Class1,因为您需要为抽象方法提供具体实现。

考虑尝试Class1在另一个泛型方法中创建一个实例。使用类型类模式时,您可以将必要的类型约束扩展到该方法的类型签名,以便编译:

def instantiateClass1[T : Factory] = new Class1[T]

如果您不需要这样做,那么您可能不需要类型类模式的全部功能。

于 2013-09-08T12:13:08.377 回答
1

当您创建泛型类或特征时,该类不会获得对您可能参数化它的任何实际类的方法的特殊访问权限。当你说

class Class1[T]

你是说

  1. 这是一个适用于未指定类型 T 的类。
  2. 它的大多数方法都将 T 类型的实例作为参数或返回 T。
  3. 只要类型参数作为 Class1 方法之一的参数出现,任何附加到类型参数的差异注释或类型边界都将被应用。
  4. 没有“Class1”类型这样的东西,但可能有任意数量的“Class1[something]”类型的派生类

就这样。您无法从 Class1 中获得对 T 的特殊访问权,因为 Scala 不知道 T 是什么。如果您希望 Class1 能够访问 T 的字段和方法,您应该扩展它或将其混合。

如果您想访问 T 的方法(不使用反射),您只能从 Class1 的一个接受 T 类型参数的方法中执行此操作。然后您将获得属于特定类型的方法的任何版本传递的实际对象。

(您可以通过反射解决此问题,但这是一个运行时解决方案,绝对不是类型安全的)。

看看你在你的原始代码片段中想要做什么......

  1. 您已指定 Class1 可以使用任意类型进行参数化。
  2. 您想使用带有单个 Int 参数的构造函数调用 T

但是你做了什么来保证 Scala 编译器 T 会有这样一个构造函数?什么都没有。那么编译器怎么能相信这个呢?好吧,它不能。

即使您添加了一个上限类型,要求 T 是某个具有此类构造函数的类的子类,这也无济于事;T 可能是一个子类,它有一个更复杂的构造函数,它回调更简单的构造函数。因此,在定义 Class1 的那一刻,编译器对使用该简单方法构造 T 的安全性没有信心。因此该调用不能是类型安全的。

基于类的 OO 并不是要从以太中变出未知类型。它不会让你把手伸进一个大礼帽形状的类加载器并拉出一个惊喜。它允许您在不知道其特定类型的情况下处理某些通用类型的任意已创建实例。在创建这些对象的时候,完全没有歧义。

于 2013-09-08T21:45:58.430 回答