逆变方法参数类型

时间:2014-03-23 04:09:44

标签: java scala covariance contravariance subtyping

wiki Contravariant_method_argument_type表示重写方法将子类型规则作为函数类型,但除了一个支持逆变量参数类型之外没有其他语言。我也无法想出使用它的任何好处。

示例:

class AnimalShelter {
    Animal getAnimalForAdoption() {      ...        }         
    void putAnimal(Animal animal) {      ...        }   
}

class CatShelter extends AnimalShelter {
    @Overriding        
    Cat getAnimalForAdoption() {  return new Cat();    }        
    @Overriding                    
    void putAnimal(Object animal) {      …        }     
}

我的问题是:

  1. 重写方法的逆变参数类型是否有用?如果是的话,它在哪里?
  2. 方法是一个功能吗?为什么Scala对函数类型和覆盖方法类型有不同的规则?

2 个答案:

答案 0 :(得分:7)

  

重写方法的逆变参数类型是否有用?如果是的话,它在哪里?

Sather documentation

翻译的示例
interface Carnivore {
  void eat(Meat food);
}

interface Herbivore {
  void eat(Plant food);
}

interface Omnivore extends Carnivore, Herbivore {
  // overrides both above eat methods,
  // since Meat and Plant are subtypes of Food
  void eat(Food food);
}
  

方法是一个功能吗?

在Scala?不,但它可以转换为函数。

  

为什么Scala对函数类型和覆盖方法类型有不同的规则?

因为重写方法类型必须遵循JVM的规则。 可以通过创建桥接方法来完成(在上面的例子中,添加方法eat(Plant)eat(Meat)只调用eat(Food)),类似于协变返回的方式类型是实现的,但它会增加语言的复杂性而没有太大的好处。

答案 1 :(得分:3)

我还可以从Spray工具包添加一个示例,特别是Marshaller特征。一般来说,您可以将Marshallers视为一个函数,将T类型的实体转换为HttpEntity(对于http响应),但有一些内部技巧,所以实际上它实现为(T, Context) => UnitHttpEntity生成Contenxt。无论如何,如果你看一下它的声明,你会发现它的类型T处于逆变位置:

trait Marshaller[-T] {
  def apply(value: T, ctx: MarshallingContext)
}

从语义上讲,您可以根据返回Unit的简单函数来思考。这里的逆差是自然的。假设您有一个简单的层次结构:

sealed trait ServerInfo {
  def data: DataTime
}

case class ServiceStatus(status: String, data: DateTime = DateTime.now) extends ServerInfo

有了这个封士:

val serverInfoMarshaller: Marshaller[ServerInfo] = ???
val serverStatusMarshaller: Marshaller[ServerStatus] = ???

你有一个返回这个状态的功能:

def response(data: ServiceStatus, marshaller: Marshaller[ServiceStatus]): Unit = ???

但是因为marshaller是逆变的,你不仅可以使用serverStatusMarshaller: Marshaller[ServerStatus],还可以使用serverInfoMarshaller: Marshaller[ServerInfo],因为它也知道如何将ServerStatus序列化为用户的正确响应。 / p>