如何在方法参数中使用泛型投影? [英] How do I use a generic type projection in a method parameter?
问题描述
我有案例类,对于每个案例类我定义了一个实体[T]隐式。这些实例定义了一个特定于每个T的Id类型。然后用一个检索方法定义一个抽象Table [T]类,该方法通过类型投影来获取T中定义的Id类型的标识符...
但它不起作用,我得到类型不匹配:
scalac generic-type- projection.scala
generic-type-projection.scala:31:error:type mismatch;
found:id.type(带底层类型实体[T] #Id)
required:_5.Id其中val _5:Entity [T]
val key =隐式[Entity [T] ] .keyFromId(id)//在'id'上输入不匹配。
^
generic-type-projection.scala:41:error:type mismatch;
found:String(dummy)
required:实体[A] #Id
t.retrieve(dummy)
^
找到两个错误
以下是代码:
import java.util.UUID
trait实体[T] {
类型Id
类型键
def getId (实体:T):Id
def keyFromId(id:Id):Key
}
case class A(f1:String,f2:Int)
object AImplicits {
隐式对象AEntity extends实体[A] {
类型Id =字符串
类型键= UUID
def getId(entity:A)= entity.f1
def keyFromId(id:String)= UUID.fromString(id)
}
}
对象Main {
抽象类Table [T:Entity ] {
def store(entity:T):Unit
def retrieve(id:Entity [T] #Id):Option [T]
}
def makeTable [T:Entity]:Table [T] =
new Table [T] {
def store(entity:T):Unit = {}
def retrieve( id:Entity [T] #Id):Option [T] = {
val key =隐式地[Entity [T]]。keyFromId(id)//在'id'上输入不匹配。
None
def main(args:Array [String]){
import AImplicits._
val t = makeTable [a]
val a = A(dummy,9)
t.store(a)
t.retrieve(dummy)//输入dummy不匹配。
}
}
任何帮助都将非常感谢。
当扩展上下文绑定速记时,类型不匹配更容易看到。请记住, [T:Entity]
语法只是需要隐含 Entity [T]
的语法糖范围。所以你的 makeTable
函数的头部其实是:
def makeTable [ T](隐式实体:实体[T]):Table [T] =
参数在函数中,函数中的隐含
调用将获取该值(实际上, new Table [T]
构造函数抓取隐式,然后 makeTable
函数的展开形式为:
def makeTable [T](隐式实体:实体[T]):Table [T] =
类的用户的id,但您想在其中一种方法中公开
new Table [T ] {
def store(entity:T):Unit = {}
def retrieve(id:Entity [T] #Id):Option [T] = {
val key = entity。 keyFromId(id)//在'id'上键入不匹配。
$ / code $ / pre
请注意,没有转换发生
id
参数检索
的类型声明。实质上,编译器抱怨entity.keyFromId
期望类型为entity.Id
的参数,但是您的id
参数是抽象类型实体[T] #Id
。
这里的问题是你想要隐藏
Entity
和Table Id
的类型。假设我有两个Table [T]
实例,但它们使用不同的实体[T]
实现 - 如此不同他们的Id
类型实际上是不同的。表实例的用户如何知道要将哪个类型传递给retrieve
函数?您可以通过向Table
类添加另一个类型参数来解决此问题。你仍然可以抽象出id / key的生成逻辑,但是这将允许编译器输入check来确保检索
函数获得正确的参数类型。 p>I have case classes and for each case class T I define an Entity[T] implicit. These instances define an Id type that is specific to each T. I then define an abstract Table[T] class with a retrieve method that takes an identifier of the Id type defined in T through a type projection...
But it does not work, I get type mismatches:
scalac generic-type-projection.scala generic-type-projection.scala:31: error: type mismatch; found : id.type (with underlying type Entity[T]#Id) required: _5.Id where val _5: Entity[T] val key = implicitly[Entity[T]].keyFromId(id) // Type mismatch on 'id'. ^ generic-type-projection.scala:41: error: type mismatch; found : String("dummy") required: Entity[A]#Id t.retrieve("dummy") ^ two errors found
Here's the code:
import java.util.UUID trait Entity[T] { type Id type Key def getId(entity: T): Id def keyFromId(id: Id): Key } case class A(f1: String, f2: Int) object AImplicits { implicit object AEntity extends Entity[A] { type Id = String type Key = UUID def getId(entity: A) = entity.f1 def keyFromId(id: String) = UUID.fromString(id) } } object Main { abstract class Table[T: Entity] { def store(entity: T): Unit def retrieve(id: Entity[T]#Id): Option[T] } def makeTable[T: Entity]: Table[T] = new Table[T] { def store(entity: T): Unit = {} def retrieve(id: Entity[T]#Id): Option[T] = { val key = implicitly[Entity[T]].keyFromId(id) // Type mismatch on 'id'. None } } def main(args: Array[String]) { import AImplicits._ val t = makeTable[A] val a = A("dummy", 9) t.store(a) t.retrieve("dummy") // Type mismatch on "dummy". } }
Any help would be very much appreciated.
解决方案The type mismatch is a bit easier to see when you expand the context bound shorthand. Remember, the
[T : Entity]
syntax is just syntactic sugar for requiring an implicitEntity[T]
to be in scope. So the header of yourmakeTable
function is actually:def makeTable[T](implicit entity: Entity[T]): Table[T] =
Now with that implicit parameter in scope, your
implicitly
call in the function will grab that value (actually, thenew Table[T]
constructor grabs the implicit, and thenimplicitly
grabs it from there), so themakeTable
function in its expanded form:def makeTable[T](implicit entity: Entity[T]): Table[T] = new Table[T] { def store(entity: T): Unit = {} def retrieve(id: Entity[T]#Id): Option[T] = { val key = entity.keyFromId(id) // Type mismatch on 'id'. None } }
Notice that there is no conversion taking place for the type declaration of the
id
parameter ofretrieve
. Essentially, the compiler is complaining thatentity.keyFromId
is expecting a parameter of typeentity.Id
, but yourid
parameter is of the abstract typeEntity[T]#Id
.The problem here is that you want to hide the
Entity
and the ids from users of theTable
class, yet you want to expose the type of theId
in one of the methods. Suppose I have two instances ofTable[T]
, but they use differentEntity[T]
implementations -- so different that theirId
types are actually different. How would the users of the table instances know which type to pass toretrieve
functions? You could fix this by adding another type parameter to theTable
class. You can still abstract away the id/key generation logic, but this will allow the compiler to type check to make sure theretrieve
function is getting the right type of parameter.这篇关于如何在方法参数中使用泛型投影?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!