Тип аргумента контравариантного метода
Вики 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) { … }
}
Мой вопрос:
- Является ли контравариантный тип аргумента переопределяющего метода полезным? если да, где это?
- Является ли метод функцией? Почему Scala имеет разные правила для типа функции и типа переопределенного метода?
2 ответа
Является ли контравариантный тип аргумента переопределяющего метода полезным? если да, где это?
Пример переведен из документации Sather:
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 имеет разные правила для типа функции и типа переопределенного метода?
Потому что переопределение типов методов должно соответствовать правилам JVM. Это может быть сделано путем создания методов моста (в случае выше, добавление методов eat(Plant)
а также eat(Meat)
который просто позвоните eat(Food)
), аналогично тому, как реализован ковариантный тип возвращаемого значения, но это усложнит язык без особой выгоды.
Я также могу добавить один пример из набора инструментов Spray, в частности черта Marshaller. В общем, вы можете думать о Marshallers как о функции с преобразованием некоторой сущности типа T
в HttpEntity
(для ответа http), но с некоторыми внутренними хитростями, так что на самом деле это реализовано как (T, Context) => Unit
, где HttpEntity
генерируется этим 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 = ???
Но поскольку маршаллер противоречив, вы также можете использовать не только serverStatusMarshaller: Marshaller[ServerStatus]
, но также serverInfoMarshaller: Marshaller[ServerInfo]
потому что он также знает, как сериализовать ServerStatus
в правильный ответ для пользователя.