是否有可能在Scala中强制调用者为多态方法指定类型参数? [英] Is it possible in Scala to force the caller to specify a type parameter for a polymorphic method?
问题描述
// API
类节点
类Person扩展节点
对象查找器
{
def find [T< Node](name:String):T = doFind(name).asInstanceOf [T]
}
//呼叫站点(正确)
val人= find [Person](joe)
//呼叫站点(在b / c推断类型内死亡,ClassCast为空)
val person = find(joe)$在客户端上面的代码中,忘记指定类型参数,因为API编写器I
>希望 表示仅返回节点。有没有什么办法来定义一个通用的方法(而不是一个类)来实现这个(或等价的)。注意:如果(manifest!= scala.reflect.Manifest.Nothing)不能编译,使用实现内部的清单来执行转换...我有一种唠叨的感觉,一些Scala向导知道如何使用Predef。<: <为此: - )
想法?
另一个解决方案是为参数指定默认类型,如下所示:
object finder {
def find [T< Node](name:String)(implicit e:T DefaultsTo Node):T =
doFind(name).asInstanceOf [T]
}
关键是定义以下
密封类DefaultsTo [A,B]
trait LowPriorityDefaultsTo {
implicit def overrideDefault [A,B] = new DefaultsTo [A,B]
}
object DefaultsTo extends LowPriorityDefaultsTo {
implicit def default [B ] = new DefaultsTo [B,B]
}
这种方法的优点是它完全避免了错误(在运行时和编译时)。如果调用者未指定类型参数,则默认为 Node
。
说明
$ b $
find
方法的签名确保只有调用者可以提供对象时才能调用它类型为 DefaultsTo [T,Node]
。当然,默认
和 overrideDefault
方法可以很容易地为任何类型Ť
。由于这些方法是隐含的,因此编译器会自动处理调用其中一个的业务,并将结果传递给 find
。 <但是,编译器如何知道要调用哪个方法?它使用类型推断和隐式解析规则来确定合适的方法。有三种情况需要考虑:
-
find
类型参数。在这种情况下,必须推断类型T
。搜索可以提供类型为DefaultsTo [T,Node]
的对象的隐式方法时,编译器会找到default
和overrideDefault
。因为它具有优先级(因为它定义在定义overrideDefault
的trait的适当子类中),所以选择default
。因此,T
必须绑定到Node
。
<使用非 -
Node
类型参数(例如>)调用find find 发现[MyObj中]( 名称)
)。在这种情况下,必须提供 DefaultsTo [MyObj,Node]
类型的对象。只有 overrideDefault
方法可以提供它,所以编译器会插入相应的调用。 使用 Node
作为类型参数调用$ c> find 。同样,任何一种方法都适用,但默认
因优先级更高而获胜。
//API
class Node
class Person extends Node
object Finder
{
def find[T <: Node](name: String): T = doFind(name).asInstanceOf[T]
}
//Call site (correct)
val person = find[Person]("joe")
//Call site (dies with a ClassCast inside b/c inferred type is Nothing)
val person = find("joe")
In the code above the client site "forgot" to specify the type parameter, as the API writer I want that to mean "just return Node". Is there any way to define a generic method (not a class) to achieve this (or equivalent). Note: using a manifest inside the implementation to do the cast if (manifest != scala.reflect.Manifest.Nothing) won't compile ... I have a nagging feeling that some Scala Wizard knows how to use Predef.<:< for this :-)
Ideas ?
Yet another solution is to specify a default type for the parameter as follows:
object Finder {
def find[T <: Node](name: String)(implicit e: T DefaultsTo Node): T =
doFind(name).asInstanceOf[T]
}
The key is to define the following phantom type to act as a witness for the default:
sealed class DefaultsTo[A, B]
trait LowPriorityDefaultsTo {
implicit def overrideDefault[A,B] = new DefaultsTo[A,B]
}
object DefaultsTo extends LowPriorityDefaultsTo {
implicit def default[B] = new DefaultsTo[B, B]
}
The advantage of this approach is that it avoids the error altogether (at both run-time and compile-time). If the caller does not specify the type parameter, it defaults to Node
.
Explanation:
The signature of the find
method ensures that it can only be called if the caller can supply an object of type DefaultsTo[T, Node]
. Of course, the default
and overrideDefault
methods make it easy to create such an object for any type T
. Since these methods are implicit, the compiler automatically handles the business of calling one of them and passing the result into find
.
But how does the compiler know which method to call? It uses its type inference and implicit resolution rules to determine the appropriate method. There are three cases to consider:
find
is called with no type parameter. In this case, typeT
must be inferred. Searching for an implicit method that can provide an object of typeDefaultsTo[T, Node]
, the compiler findsdefault
andoverrideDefault
.default
is chosen since it has priority (because it's defined in a proper subclass of the trait that definesoverrideDefault
). As a result,T
must be bound toNode
.find
is called with a non-Node
type parameter (e.g.,find[MyObj]("name")
). In this case, an object of typeDefaultsTo[MyObj, Node]
must be supplied. Only theoverrideDefault
method can supply it, so the compiler inserts the appropriate call.find
is called withNode
as the type parameter. Again, either method is applicable, butdefault
wins due to its higher priority.
这篇关于是否有可能在Scala中强制调用者为多态方法指定类型参数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!