指定泛型类型的要求

时间:2013-09-08 11:35:24

标签: scala

我想调用泛型类型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
   }
 }

2 个答案:

答案 0 :(得分:5)

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

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

首先,定义一个trait,它将是一个用于创建T给定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)
}

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

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]


为了响应您的更新(使用抽象的creator方法),这是一种非常合理的方法,只要您不介意为每个{Class1创建一个子类型T {1}}。另请注意,T在您想要创建Class1实例的任何位置都需要是具体类型,因为您需要为抽象方法提供具体实现。

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

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

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

答案 1 :(得分:1)

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

class Class1[T]

你在说

  1. 这是一个适用于未指定类型T的类。
  2. 它的大多数方法都会将T类型的实例作为参数或返回T.
  3. 每当它作为Class1方法之一的参数出现时,将应用附加到type参数的任何方差注释或类型边界。
  4. 没有类型“Class1”这样的东西,但可能有任意数量的类型为“Class1 [something]”的派生类
  5. 这就是全部。您无法从Class1中获得对T的特殊访问权限,因为Scala不知道T是什么。如果你希望Class1能够访问T的字段和方法,你应该扩展它或将它混合进来。

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

    (你可以使用反射来解决这个问题,但这是一个运行时解决方案,绝对类型安全)。

    查看您在原始代码段中尝试执行的操作...

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

      但是你做了什么来承诺Scala编译器T会有这样的构造函数?什么都没有。那么编译器如何信任呢?好吧,它不能。

      即使你添加了一个上层类型绑定,要求T是某个具有这样一个构造函数的类的子类,这也没有帮助; T可能是一个子类,它有一个更复杂的构造函数,它调用更简单的构造函数。因此在定义Class1的位置时,编译器对使用这种简单方法构造T的安全性没有信心。因此,调用不能是类型安全的。

      基于类的OO并不是要从以太中提取未知类型;它不会让你把手伸进顶帽式的装载机并带出惊喜。它允许您在不知道其特定类型的情况下处理某些常规类型的任意已创建实例。在那些创建的对象时,根本就没有歧义。