在以下示例中,我尝试在MySource
和TypedPipe[T]
之间创建隐式转换。我拥有MySource
,事实上我有很多这样的来源,所以我想使用Porable[T]
特征来标记我想要输出T
的类型参数TypedPipe[T]
,以便隐式转换可以自动执行.toTypedPipe[T]
部分(这样我就不必为我使用它时的每个来源写.toTypedPipe[T]
。)
import scala.language.implicitConversions
// The following two are pre-defined and I cannot change them
class TypedPipe[T](val path: String) {
def mapWithValue = {
println("values from " + path + " of type " + this.getClass)
}
}
class Source(val path: String) {
def toTypedPipe[T] = { new TypedPipe[T](path) }
}
// The following are defined by me, so yes I can change them.
trait Portable[T]
class MySource(p: String) extends Source(p) with Portable[Tuple2[Int, Long]]
object conversions {
implicit def portableSourceToTypedPipe[S <: Source with Portable[T], T](source: S): TypedPipe[T] = {
source
.toTypedPipe[T]
}
}
import conversions._
portableSourceToTypedPipe(new MySource("real_path")).mapWithValue
但问题是,Scala似乎无法推断T
的最后声明:
scala> import scala.language.implicitConversions
import scala.language.implicitConversions
scala> class TypedPipe[T](val path: String) {
| def mapWithValue = {
| println("values from " + path + " of type " + this.getClass)
| }
| }
defined class TypedPipe
scala> class Source(val path: String) {
| def toTypedPipe[T] = { new TypedPipe[T](path) }
| }
defined class Source
scala>
scala> trait Portable[T]
defined trait Portable
scala>
scala> class MySource(p: String) extends Source(p) with Portable[Tuple2[Int, Long]]
defined class MySource
scala> object conversions {
| implicit def portableSourceToTypedPipe[S <: Source with Portable[T], T](source: S): TypedPipe[T] = {
| source
| .toTypedPipe[T]
| }
| }
defined module conversions
scala> import conversions._
import conversions._
scala> portableSourceToTypedPipe(new MySource("real_path")).mapWithValue
<console>:17: error: inferred type arguments [MySource,Nothing] do not conform to method portableSourceToTypedPipe's type parameter bounds [S <: Source with Portable[T],T]
portableSourceToTypedPipe(new MySource("real_path")).mapWithValue
^
<console>:17: error: type mismatch;
found : MySource
required: S
portableSourceToTypedPipe(new MySource("real_path")).mapWithValue
^
scala>
从示例中,MySource
实现Portable[Tuple2[Int, Long]]
非常明显,因此T
应为Tuple2[Int, Long]
。为什么不以这种方式推断出来(这会使这个例子有效)?
修改
在this question and its answer提到n.m.之后,我修改了我的代码,使用隐式证据参数来表达两个类型参数之间的关系。显式转换调用现在有效,但不是隐式转换调用。所以仍然需要帮助。
scala> object conversions {
| implicit def portableSourceToTypedPipe[S, T](source: S)(implicit ev: S <:< Source with Portable[T]): TypedPipe[T] = {
| source
| .toTypedPipe[T]
| }
| }
defined module conversions
scala> import conversions._
import conversions._
scala> portableSourceToTypedPipe(new MySource("real_path")).mapWithValue
values from real_path of type class $line4.$read$$iw$$iw$TypedPipe
scala> (new MySource("real_path")).mapWithValue
<console>:17: error: Cannot prove that MySource <:< Source with Portable[T].
(new MySource("real_path")).mapWithValue
^
<console>:17: error: value mapWithValue is not a member of MySource
(new MySource("real_path")).mapWithValue
EDIT2
我选择特征Portable[T]
的原因是它可以使用多种基本Source
类型。熟悉Scalding的人可能知道我们有很多类型的来源,例如: DailySuffixSource,HourlySuffixSource,更不用说可以插入其他特征,如SuccessFileSource
和DelimitedScheme
。必须为每个基础源/特征组合实现某些东西需要相当多的工作。因此我的特质选择。当然,这不是必须的 - 任何可以使用O(1)实施量的多个基本源/特征组合的答案都可以。
答案 0 :(得分:3)
鉴于您没有在返回类型中的任何位置使用类型参数S,为什么不让portableSourceToTypedPipe
获取Source with Portable[T]
。
换句话说:
implicit def portableSourceToTypedPipe[T](source: Source with Portable[T]): TypedPipe[T]
这可以解决您的编译问题。 通常,您越明确,编译器可以解析由类型参数表示的约束的可能性就越高。这首先要完全删除不必要的类型参数。
答案 1 :(得分:1)
您的Source
定义表明您可以致电toTypedPipe[T]
以获取任何 T
。如果您确实希望MySource
仅转换为Tuple2[Int, Long]
,则应为
class TypedSource[T](path: String) extends Source(path) {
def toSpecificTypedPipe = toTypedPipe[T]
}
class MySource(p: String) extends TypedSource[(Int, Long)](p)
implicit def portableSourceToTypedPipe[T](source: TypedSource[T]): TypedPipe[T] =
source.toSpecificTypedPipe
(您也可以使用合成而不是继承TypedSource
。)
如果您确实希望能力转换为任何一种“首选”类型,只需摆脱S
,您就不需要它了:
implicit def portableSourceToTypedPipe[T](source: Source with Portable[T]): TypedPipe[T]