Scala 中的抽象类型/类型参数 [英] Abstract Types / Type Parameters in Scala

查看:54
本文介绍了Scala 中的抽象类型/类型参数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试编写一些需要执行以下操作的 Scala 代码:

I am trying to write some Scala code that needs to do something like:

class Test[Type] { 
   def main {
       SomeFunc classOf[Type]
       val testVal: Type = new Type()
    }
 }

它失败了.我显然不了解 Scala 泛型参数.显然,误解是在 C++ 中,模板的功能本质上类似于字符串替换,因此只要传入的类具有默认构造函数, new Type() 就可以工作.然而,在 Scala 中,类型是不同种类的对象.

and it's failing. I'm obviously not understanding something about Scala generic parameters. Clearly, the misunderstanding is that in C++, templates essentially function like string substitutions, so new Type() will work as long as the class being passed in has a default constructor. However, in Scala, types are different kinds of objects.

推荐答案

正如您所指出的,C++ 有模板.简而言之,C++ 说有一个针对所有类型 T 的测试,使得 Test 可以编译."这使得在 T 上隐式添加约束变得容易,但不利的一面是它们是隐式的,并且您的类的用户可能很难在不阅读代码的情况下理解.

As you point out, C++ has templates. In short, C++ says "there is a Test for all types T such that Test compiles." That makes it easy to implicitly add constraints on T, but on the down side they're implicit and may be hard for a user of your class to understand without reading code.

Scala 的参数多态性(又名泛型)更像 ML、Haskell、Java 和 C#.在 Scala 中,当您编写class Test[T]"时,您是在说对于所有 T 都存在没有约束的类型 Test[T]".正式推理更简单,但这确实意味着您必须明确约束.例如,在 Scala 中,您可以说class Test[T <: Foo]"来表示 T 必须是 Foo 的子类型.

Scala's parametric polymorphism (aka generics) work much more like ML, Haskell, Java, and C#. In Scala, when you write "class Test[T]" you are saying "for all T there exists a type Test[T]" without constraint. That's simpler to reason about formally, but it does mean that you have to be explicit about constraints. For instance, in Scala you can say "class Test[T <: Foo]" to say that T must be a subtype of Foo.

C# 有一种方法可以向 T 添加关于构造函数的约束,但不幸的是 Scala 没有.

C# has a way to add a constraint to T regarding constructors, but unfortunately Scala does not.

在 Scala 中有几种方法可以解决您的问题.一个是类型安全的,但更冗长.另一个不是类型安全的.

There are a couple of ways to solve your problem in Scala. One is typesafe but a bt more verbose. The other is not typesafe.

类型安全的方式看起来像

The typesafe way looks like

class Test[T](implicit val factory : () => T) {
  val testVal = factory
}

然后你可以有一个工厂主体,用于你的系统中有用的类型

Then you can have a body of factories for types useful in your system

object Factories {
  implicit def listfact[X]() = List[X]()
  implicit def setfact[X]() = Set[X]()
  // etc
}

import Factories._
val t = new Test[Set[String]]

如果您的库的用户需要他们自己的工厂,那么他们可以添加他们自己的 Factories 对象.这种解决方案的一个优点是可以使用任何带有工厂的东西,无论是否有无参数构造函数.

If users of your library need their own factories then they can add their own equivalent of the Factories object. One advantage to this solution is that anything with a factory can be used, whether or not there's a no-arg constructor.

不那么类型安全的方法使用反射和 Scala 中称为清单的功能,这是一种绕过 Java 类型擦除约束的方法

The not-so-typesafe way uses reflection and a feature in Scala called manifests which are a way to get around a Java constraint regarding type erasure

 class Test[T](implicit m : Manifest[T]) {
   val testVal = m.erasure.newInstance().asInstanceOf[T]
 }

这个版本你还是写

class Foo
val t = new Test[Foo]

但是,如果没有可用的无参数构造函数,您会得到运行时异常而不是静态类型错误

However, if there's no no-arg constructor available you get a runtime exception instead of a static type error

scala> new Test[Set[String]] 
java.lang.InstantiationException: scala.collection.immutable.Set
at java.lang.Class.newInstance0(Class.java:340)

这篇关于Scala 中的抽象类型/类型参数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆