在 Scala 中,如何使抽象类型的内部类型对不同的实例具有不变性?
In scala, how to make inner type of an abstract type invariant to different instances?
在某些情况下,我想使用内部类型来替代泛型类型,例如在下面的例子中,我没有定义 Dependent[T <: BaseType]
,而是这样定义的:
abstract class BaseType {
type N
def create(v: Int): N
class Dependent(val n: N) {
def +(other: Dependent) = new Dependent(create(n.hashCode() + n.hashCode()))
}
}
object BaseType {
class Aggregator[T <: BaseType](val seq: Seq[T#Dependent]) {
def result() = seq.reduce(_ + _)
}
}
显然这不会编译,因为 Dependent
现在是一个内部 class,并且在不同的 BaseType 实例下创建的不同 Dependent
不能相互操作。 (BTW 会抛出以下编译错误)
Error:(15, 35) type mismatch;
found : T#Inner
required: _33.Inner where val _33: T
def result() = seq.reduce(_ + _)
然而,这个问题在 scala 中应该有一个简单的解决方案,因为在 java 中,这样的任务可以通过以下方式轻松完成:
static class Dependent { ...
scala 中的代码比 java 更长是没有意义的。 scala 中的一个直接类比是在 BaseType 中添加强制规则,这要求其所有实现都是 object/singleton 而不是 class。但是我还没有看到这样的功能。
所以问题是,在 java 中可以轻松完成同样的事情,最短的方法是什么?
UPDATE 显然我不太清楚我的意图,我想要的不是按原样使用 class Dependent
,而是扩展 BaseType这样 BaseType
作为 Dependent
的类型参数,例如如果我定义 2 个对象:
object Sub1 extends BaseType {
override type N = Long
override def create(v: Int): N = v.toLong
}
object Sub2 extends BaseType {
override type N = Double
override def create(v: Int): N = v.toDouble
}
然后 +
符号和 Aggregator
都可以用于 2 Dependent 只有当它们来自同一个对象时,这意味着这将成功:
assert(
new Aggregator(
Seq(
new Sub1.Dependent(1),
new Sub1.Dependent(2)
)).result() == new Sub1.Dependent(1) +
new Sub1.Dependent(2)
)
但这会失败:
assert(
new Aggregator(
Seq(
new Sub1.Dependent(1),
new Sub2.Dependent(2)
)).result() == new Sub1.Dependent(1) +
new Sub2.Dependent(2)
)
因为 2 个依赖实例是不同类型的,但是使用最新的 scala 编译器,即使第一个也会失败,因为在定义聚合器时它不知道 T <: BaseType
是单例。
我不确定这是否是您想要的,可能您将实际问题过度简化了。
但是,如果不是,这不是解决了你的问题吗?
sealed trait BaseType[N] {
def create(v: Int): N
}
object BaseType {
final implicit val LongBase: BaseType[Long] = new BaseType[Long] {
override def create(v: Int): Long = v.toLong
}
final implicit val DoubleBase: BaseType[Double] = new BaseType[Double] {
override def create(v: Int): Double = v.toDouble
}
final class Aggregator[N : BaseType](val seq: Seq[Depedent[N]]) {
def result: Depedent[N] = seq.reduce(_ + _)
}
}
final class Depedent[N](val n: N)(implicit base: BaseType[N]) {
def + (that: Depedent[N]): Depedent[N] =
new Depedent(base.create(this.n.hashCode + that.n.hashCode))
}
如果这不能满足您的问题,请发表评论说明为什么不能。
because in java such task can be easily accomplished by:
static class Dependent { ...
不,它没有完成任务,因为这个Dependent
不能使用N
(即使它是BaseType
的类型参数)。您需要 static class Dependent<N>
,而 Scala 等价物是
object BaseType {
class Dependent[N]
}
如果您还没有伴随对象,这确实比 Java 稍长,但这并不是 Scala 设计中的要求,它可以做 Java 可以做的任何事情更短的代码(或者根本没有,真的)。
如果你愿意
Then both + sign and Aggregator can be used on 2 Dependent only if they are from the same object, which means this will succeed
这可以做到:
class Aggregator[T <: BaseType with Singleton](val seq: Seq[T#Dependent]) {
def result() = seq.reduce(_ + _)
}
在某些情况下,我想使用内部类型来替代泛型类型,例如在下面的例子中,我没有定义 Dependent[T <: BaseType]
,而是这样定义的:
abstract class BaseType {
type N
def create(v: Int): N
class Dependent(val n: N) {
def +(other: Dependent) = new Dependent(create(n.hashCode() + n.hashCode()))
}
}
object BaseType {
class Aggregator[T <: BaseType](val seq: Seq[T#Dependent]) {
def result() = seq.reduce(_ + _)
}
}
显然这不会编译,因为 Dependent
现在是一个内部 class,并且在不同的 BaseType 实例下创建的不同 Dependent
不能相互操作。 (BTW 会抛出以下编译错误)
Error:(15, 35) type mismatch;
found : T#Inner
required: _33.Inner where val _33: T
def result() = seq.reduce(_ + _)
然而,这个问题在 scala 中应该有一个简单的解决方案,因为在 java 中,这样的任务可以通过以下方式轻松完成:
static class Dependent { ...
scala 中的代码比 java 更长是没有意义的。 scala 中的一个直接类比是在 BaseType 中添加强制规则,这要求其所有实现都是 object/singleton 而不是 class。但是我还没有看到这样的功能。
所以问题是,在 java 中可以轻松完成同样的事情,最短的方法是什么?
UPDATE 显然我不太清楚我的意图,我想要的不是按原样使用 class Dependent
,而是扩展 BaseType这样 BaseType
作为 Dependent
的类型参数,例如如果我定义 2 个对象:
object Sub1 extends BaseType {
override type N = Long
override def create(v: Int): N = v.toLong
}
object Sub2 extends BaseType {
override type N = Double
override def create(v: Int): N = v.toDouble
}
然后 +
符号和 Aggregator
都可以用于 2 Dependent 只有当它们来自同一个对象时,这意味着这将成功:
assert(
new Aggregator(
Seq(
new Sub1.Dependent(1),
new Sub1.Dependent(2)
)).result() == new Sub1.Dependent(1) +
new Sub1.Dependent(2)
)
但这会失败:
assert(
new Aggregator(
Seq(
new Sub1.Dependent(1),
new Sub2.Dependent(2)
)).result() == new Sub1.Dependent(1) +
new Sub2.Dependent(2)
)
因为 2 个依赖实例是不同类型的,但是使用最新的 scala 编译器,即使第一个也会失败,因为在定义聚合器时它不知道 T <: BaseType
是单例。
我不确定这是否是您想要的,可能您将实际问题过度简化了。
但是,如果不是,这不是解决了你的问题吗?
sealed trait BaseType[N] {
def create(v: Int): N
}
object BaseType {
final implicit val LongBase: BaseType[Long] = new BaseType[Long] {
override def create(v: Int): Long = v.toLong
}
final implicit val DoubleBase: BaseType[Double] = new BaseType[Double] {
override def create(v: Int): Double = v.toDouble
}
final class Aggregator[N : BaseType](val seq: Seq[Depedent[N]]) {
def result: Depedent[N] = seq.reduce(_ + _)
}
}
final class Depedent[N](val n: N)(implicit base: BaseType[N]) {
def + (that: Depedent[N]): Depedent[N] =
new Depedent(base.create(this.n.hashCode + that.n.hashCode))
}
如果这不能满足您的问题,请发表评论说明为什么不能。
because in java such task can be easily accomplished by:
static class Dependent { ...
不,它没有完成任务,因为这个Dependent
不能使用N
(即使它是BaseType
的类型参数)。您需要 static class Dependent<N>
,而 Scala 等价物是
object BaseType {
class Dependent[N]
}
如果您还没有伴随对象,这确实比 Java 稍长,但这并不是 Scala 设计中的要求,它可以做 Java 可以做的任何事情更短的代码(或者根本没有,真的)。
如果你愿意
Then both + sign and Aggregator can be used on 2 Dependent only if they are from the same object, which means this will succeed
这可以做到:
class Aggregator[T <: BaseType with Singleton](val seq: Seq[T#Dependent]) {
def result() = seq.reduce(_ + _)
}