是否有可能在Scala中强制调用者为多态方法指定类型参数? [英] Is it possible in Scala to force the caller to specify a type parameter for a polymorphic method?

查看:102
本文介绍了是否有可能在Scala中强制调用者为多态方法指定类型参数?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

  // 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



<但是,编译器如何知道要调用哪个方法?它使用类型推断和隐式解析规则来确定合适的方法。有三种情况需要考虑:


  1. find 类型参数。在这种情况下,必须推断类型 T 。搜索可以提供类型为 DefaultsTo [T,Node] 的对象的隐式方法时,编译器会找到 default overrideDefault 。因为它具有优先级(因为它定义在定义 overrideDefault 的trait的适当子类中),所以选择 default 。因此, T 必须绑定到 Node


  2. <使用非 - 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:

  1. find is called with no type parameter. In this case, type T must be inferred. Searching for an implicit method that can provide an object of type DefaultsTo[T, Node], the compiler finds default and overrideDefault. default is chosen since it has priority (because it's defined in a proper subclass of the trait that defines overrideDefault). As a result, T must be bound to Node.

  2. find is called with a non-Node type parameter (e.g., find[MyObj]("name")). In this case, an object of type DefaultsTo[MyObj, Node] must be supplied. Only the overrideDefault method can supply it, so the compiler inserts the appropriate call.

  3. find is called with Node as the type parameter. Again, either method is applicable, but default wins due to its higher priority.

这篇关于是否有可能在Scala中强制调用者为多态方法指定类型参数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆