scala抽象类型可以保存类型参数吗?

时间:2018-03-14 08:31:30

标签: scala

我有以下简单的演示代码,它给出了类型不匹配错误。 我完全不明白为什么会发生这种错误。有人会告诉我如何解决这个错误。进一步解释或相关的学习资源将非常感谢。

object Application extends App {
  private val foo = new Foo
  private val holder = new FooTypeHolder
  private val client = new Client(holder)
  client.showElementType(foo)
}

class Foo

trait TypeHolder[T] {
  type ElementType = T
}

class FooTypeHolder extends TypeHolder[Foo]

class Client[T <: TypeHolder[_]](val holder: T) {

  def showElementType(t: T#ElementType): Unit = {
    println("show element type " + t.toString)
    println(t.getClass)
  }
}

编译错误为:

Error:(10, 26) type mismatch;
 found   : Application.foo.type (with underlying type Foo)
 required: _$1
  client.showElementType(foo)

2018-03-15更新

就像Andrey Tyukin对TypeHolder用于什么感到好奇一样,它只包含T之外的任何内容。我在这个特性上添加了一些功能。

代码如下。

object Application extends App {
  private val foo = new Foo
  private val holder = new FooTypeHolder
  private val client = new Client(holder)
  client.showElementType(foo)
}

class Foo

trait TypeHolder[T] {
  type ElementType = T

  def holderFun(): Unit = println(s"holder, type:${getClass}")

  def elementFun(ele: ElementType): Unit = println(s"element, type:${getClass}")
}

class FooTypeHolder extends TypeHolder[Foo]

class Client[T <: TypeHolder[_]](val holder: T) {

  def showElementType(t: T#ElementType): Unit = {
    println("show element type " + t.toString)
    holder.holderFun()
    holder.elementFun(t)
  }

}

给出如下编译错误:

Error:(10, 26) type mismatch;
 found   : o2.Application.foo.type (with underlying type o2.Foo)
 required: _$1
  client.showElementType(foo)
Error:(30, 23) type mismatch;
 found   : _$1
 required: Client.this.holder.ElementType
    (which expands to)  _$1
    holder.elementFun(t)

总的来说,我想知道的核心是:

当我通过client实例化val client = new Client(holder)时 而holder的类型为:TypeHolder[Foo] scala编译器是否可以推断ElementType的{​​{1}}是“hoder”?

非常感谢@Andrey提供各种可行的解决方案。 我检查你的代码片段1~3,并仔细比较我的代码,这让我现在更清楚了。不过我还有一些问题。

第3版

此版本的代码最接近我的原始邮政编码。唯一的区别是函数Foo

的类型限制
  • 我的:showElementType
  • 你的:def showElementType(t: T#ElementType): Unit

让我感到困惑的是,因为“def showElementType(t: holder.ElementType): Unit”有效,“(t: holder.ElementType)”也应该有用吗?事实上并非如此。为什么呢?

第2版

这里的区别是(t: T#ElementType)的位置。我把它放在特征定义中,而你把它放在类型参数限制中。

  • 我的:type ElementType = T
  • 你的:trait TypeHolder[T] { type ElementType = T}

我无法理解为什么特质定义中的赋值语句不起作用。

版本1

您使用两个类型参数class Client[T, H <: TypeHolder { type ElementType = T }]&amp; T分别捕获HFoo的类型。它应该绝对有效。客户端的类型也与TypeHolder相对应。我的问题是,是否可以定义客户端只使用一个类型参数,例如Client[Foo, TypeHolder[Foo]]。换句话说,编译器是否可以从Client[TypeHolder[Foo]]推断TypeHolder[Foo]ElementType

谢谢。

2 个答案:

答案 0 :(得分:2)

经过一些修改后编译:

object Application extends App {
  private val foo = new Foo
  private val holder = new FooTypeHolder
  private val client = new Client(holder)
  // not sure why you try to use this one
  // client.showElementType(foo)
  // foo doesn't extend the Typeholder, it's just regular class

  // on the other hand this will compile
  client.showElementType(holder)
}

class Foo

trait TypeHolder[T] {
  type ElementType = T
}

class FooTypeHolder extends TypeHolder[Foo]

class Client[T <: TypeHolder[_]](val holder: T) {

  // no need to use the #ElementType
  def showElementType(t: T): Unit = {
    println("show element type " + t.toString)
    println(t.getClass)
  }
}

希望这也有帮助:

What does the `#` operator mean in Scala?

https://github.com/ghik/opinionated-scala/wiki/Generics-and-type-members

就错误_ $ 1而言,它只是编译器说:&#34;我无法解决这个问题&#34;

答案 1 :(得分:2)

我不确定你在这里尝试了什么,但这里是我的版本&#34;可能最接近的代码投影到可编辑的Scala&#34;:

import scala.language.higherKinds
object Application extends App {
  private val foo = new Foo
  private val holder = new FooTypeHolder
  private val client = new Client(holder)
  client.showElementType(foo)
}

class Foo

trait TypeHolder[T] {
  type ElementType = T
}

class FooTypeHolder extends TypeHolder[Foo]

class Client[T, H[X] <: TypeHolder[X]](val holder: H[T]) {
  def showElementType(t: T): Unit = {
    println("show element type " + t.toString)
    println(t.getClass)
  }
}

但也可能是这样:

import scala.language.higherKinds
object Application extends App {
  private val foo = new Foo
  private val holder = new FooTypeHolder
  private val client = new Client[Foo, FooTypeHolder](holder)
  client.showElementType(foo)
}

class Foo

trait TypeHolder {
  type ElementType
}

class FooTypeHolder extends TypeHolder {
  type ElementType = Foo
}

class Client[T, H <: TypeHolder { type ElementType = T }](val holder: H) {
  def showElementType(t: T): Unit = {
    println("show element type " + t.toString)
    println(t.getClass)
  }
}

或者这个?:

import scala.language.higherKinds
object Application extends App {
  private val foo = new Foo
  private val holder = new FooTypeHolder
  private val client = new Client(holder)
  client.showElementType(foo)
}

class Foo

trait TypeHolder[T] {
  type ElementType = T
}

class FooTypeHolder extends TypeHolder[Foo]

class Client[H <: TypeHolder[_]](val holder: H) {
  def showElementType(t: holder.ElementType): Unit = {
    println("show element type " + t.toString)
    println(t.getClass)
  }
}

我从根本上不了解你的代码是:TypeHolder[T]应该做什么,究竟是什么?整个TypeHolder[T]构造似乎不包含T以外的任何信息,为什么不直接使用T,这样?:

object Application extends App {
  private val foo = new Foo
  private val client = new Client[Foo]
  client.showElementType(foo)
}

class Foo

class Client[T] {
  def showElementType(t: T): Unit = {
    println("show element type " + t.toString)
    println(t.getClass)
  }
}

一些一般提示:

  • 避免一次提出多个问题
  • 避免在同一帖子中询问有关您之前问题的答案之一的多个问题。
  • 这可能需要一些繁重的编辑......

修改

我只是尝试以与您已将其添加到您的帖子中的顺序相同的顺序回答所有其他问题:

1。它适用于holder.ElementType,因为当您实例化Client时,holder已知为TypeHolder[Foo]类型,因此我们知道holder.ElementType = Foo

2。它对T#ElementType的{​​{1}}不起作用,因为正如签名中的存在主义所说,T <: TypeHolder[_]可以是{{1} } T未指定TypeHolder

forSome

因此ElementType被设置为一些奇怪的合成存在类型T <: TypeHolder[_$1] forSome { type _$1 } T#ElementType = (TypeHolder[_])#ElementType = (TypeHolder[_$1] forSome { type _$1 })#ElementType = _$1 ,并且您得到的错误消息看起来有点像这样:

T#ElementType

因此,将其设置为存在类型是完全无用的,您将永远无法找到满足此奇怪类型约束的术语。你甚至不能强行转换为这种类型,因为这种类型甚至没有一个名称可以在代码中的任何地方引用它。

说实话:这似乎完全是垃圾。整个预测根本不应该编译,为什么世界上任何人都希望存在量化的类型从量词下逃脱?这可能应该报告为编译器问题。

3. 定义

_$1

有效,因为它还保持类型typeHolder.scala:6: error: type mismatch; found : Main.foo.type (with underlying type Foo) required: _$1 client.showElementType(foo) ^ 显式。如果你更换它 由存在主义

class Client[T, H <: TypeHolder { type ElementType = T }]

然后,Tclass Client[T, H <: TypeHolder { type ElementType = X forSome { type X }}] 之间的每个连接都会丢失,并且会因上述T的原因而失败:您无法替换元素类型由一些未知的存在主义,然后希望能够将TypeHolder传递给它。

4. On&#34;是否可以定义客户端只使用一个类型参数?&#34; - 当然,为什么不呢?

T#ElementType

不要将任何类型参数添加到Foo,因此您不必在以后通过存在来删除它们。