Scala:如何指定暗示相等的类型参数边界?
不要被长文本吓倒,这些要点非常琐碎,但需要一些代码来说明问题。 :-)
设置:
假设我想创建一个特征,这里建模为某种Converter
,它本身是通用的,但有一个类型化方法Convert()
返回一个适当类型的结果对象,例如 Container[T]
:
trait Converter {
def convert[T]( input: T ) : Container[T]
}
trait Container[T] // details don't matter
我的问题是关于方法的类型约束,特别是强制相等性,并且有两个密切相关的部分。
第 1 部分:现在说有一种专门的容器类型特别适合基于数组的内容,如下所示:
object Container {
trait ForArrays[U] extends Container[Array[U]]
}
考虑到这种可能性,我现在想专门化转换器,特别是返回convert()
方法的类型,到专门的 Container.ForArrays
类型:
object Converter {
trait ForArrays extends Converter {
// the following line is rubbish - how to do this right?
def convert[E,T <: Array[E]]( input: T ) : Container.ForArrays[E]
}
}
这样我就可以做这样的事情:
val converter = new Converter.ForArrays { ... }
val input = Array( 'A', 'B', 'C' )
val converted : Container.ForArrays[Char] = converter.convert( input )
基本上我想要 Scala,如果转换器的类型是已知是Converter.ForArrays
,还将 convert[Char]()
的专门返回类型推断为 Container.ForArrays[Char]
,即匹配容器类型加上输入的数组类型。这或类似的事情可能吗?如果可以,我该怎么做?例如,如何指定 Convert() 上的类型参数/界限(提供的只是一个替代品 - 我不知道如何做到这一点)。哦,当然,它仍然重写它的 super 方法,否则什么也得不到。
第 2 部分: 作为后备方案,如果这不可能,我当然可以将转换函数推入以数组为中心的变体中,如下所示:
trait Converter // now pretty useless as a shared trait
object Converter {
trait ForValues extends Converter {
def convert[T]( input: T ) : Container[T]
}
trait ForArrays extends Converter {
def convert[E]( input: Array[E] ) : Container.ForArrays[E]
}
}
好的。现在假设我有一个更专业的 Converter.ForArrays.SetBased
,它可以在内部使用一组 E 类型的元素(与“输入”数组元素类型相同)在转换。然而,该集合现在是特征的一个参数,如下所示:
case class SetBased( set: Set[F] ) extends Converter.ForArrays {
// the following line is also rubbish...
def convert[E = F]( input: Array[E] ) : Container.ForArrays[E] = {...}
}
同样,这是关于 Convert() 方法的类型参数。这里的困难是:如何将类的类型参数 - F
- 粘合到方法的类型参数 - E
- 这样 Scala 编译器只会让用户使用其元素与集合的元素匹配的数组调用 convert()
?例子:
val set = Set( 'X', 'Y', 'Z' )
val converter = new Converter.ForArrays.SetBased( set )
val input = Array( 'A', 'B', 'C' )
val converted : Container.ForArrays[Char] = converter.convert( input )
Don't be put off by the long text, the points are quite trivial but require a bit of code to illustrate the problem. :-)
The Setup:
Say I would like to create a trait, here modeled as a Converter
of some kind, that itself is generic but has a typed method convert()
that returns an appropriately typed result object, say a Container[T]
:
trait Converter {
def convert[T]( input: T ) : Container[T]
}
trait Container[T] // details don't matter
My question is about type constraints on methods, in particular for enforcing equality, and has two closely related parts.
Part 1: Say now that there was a specialized container type that was particularly suitable for array-based contents, like so:
object Container {
trait ForArrays[U] extends Container[Array[U]]
}
Given this possibility, I'd now like to specialize the Converter and in particular the return type of the convert()
method, to the specialized Container.ForArrays
type:
object Converter {
trait ForArrays extends Converter {
// the following line is rubbish - how to do this right?
def convert[E,T <: Array[E]]( input: T ) : Container.ForArrays[E]
}
}
So that I can do something like this:
val converter = new Converter.ForArrays { ... }
val input = Array( 'A', 'B', 'C' )
val converted : Container.ForArrays[Char] = converter.convert( input )
Basically I want Scala, if the type of converter is known to be Converter.ForArrays
, to also infer the specialized return type of convert[Char]()
as Container.ForArrays[Char]
, i.e. the matching container type plus the array type of the input. Is this or something like it possible and if so, how do I do it? E.g. how do I specify the type parameters / bounds on convert() (what is provided is just a stand-in - I have no idea how to do this). Oh, and naturally so that it still overrides its super method, otherwise nothing is gained.
Part 2: As a fallback, should this not be possible, I could of course push the convert function down into the Array-focused variant, like so:
trait Converter // now pretty useless as a shared trait
object Converter {
trait ForValues extends Converter {
def convert[T]( input: T ) : Container[T]
}
trait ForArrays extends Converter {
def convert[E]( input: Array[E] ) : Container.ForArrays[E]
}
}
OK. Now say I have an even more specialized Converter.ForArrays.SetBased
that can internally use a set of elements of type E (the same as the 'input' array element type) to do some particular magic during the conversion. The set is now a parameter of the trait, however, like so:
case class SetBased( set: Set[F] ) extends Converter.ForArrays {
// the following line is also rubbish...
def convert[E = F]( input: Array[E] ) : Container.ForArrays[E] = {...}
}
Again, this is about the type parameters of the convert() method. The difficulty here is: how do I glue the type parameter of the class - F
- to the type parameter of the method - E
- such that the Scala compiler will only let the user call convert()
with an array whose elements match the elements of the set? Example:
val set = Set( 'X', 'Y', 'Z' )
val converter = new Converter.ForArrays.SetBased( set )
val input = Array( 'A', 'B', 'C' )
val converted : Container.ForArrays[Char] = converter.convert( input )
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
![扫码二维码加入Web技术交流群](/public/img/jiaqun_03.jpg)
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
不,你不能。出于同样的原因,您在重写方法时不能缩小参数类型或扩大返回类型(但可以缩小返回类型)。但是,您可以执行以下操作(对于您的后备解决方案):
No, you can't. For the same reason you can't narrow argument types or widen return types when overriding a method (but can narrow return type). Here is what you can do, however (for your fallback solution):
我假设 Container.ForArrays 是 Container 的子类,没有这个,Converter.ForArrays.convert 将不匹配被覆盖的
Converter.convert
的签名尝试编写如下内容:
关于您的后备解决方案。如果两个类型相同,则只需使用相同的类型参数!
I'm going to assume that
Container.ForArrays
is a subclass ofContainer
, without this,Converter.ForArrays.convert
won't match the signature of the overriddenConverter.convert
Try writing it something like this:
Regarding your fallback solution. If two types are the same, then just use the same type param!