Ко/Контр вариантность

В язке Java есть несколько типов данных, рассмотрим следующие
object- базовый типnumber- для всех числовых типов данных, наследуется от objectint- для целых числовых типов, наследуется от number, занимает 4 байтаdouble- для дробных числовых типов, наследуется от number, занимает 8 байт
На картинке отображены 8 переменных:
o1,o2- типobjectn1,n2- типnumberi1,i2- типintd1,d2- типdouble
Операция присвоения
В основе правил присваения следующее:
Для переменной x типа X, можно присвоить значение типа X или дочернего типа данных X
т.е. для переменной n1 допустимы операции:
n1 = n2 // т.е. number тип полностью совметим с самим с собой (number)
n1 = i1 // т.е. int расширяет number,
n1 = i2 // и все операции над number доступны и для int
n1 = d1 // т.е. double расширяет number,
n1 = d2 // и все операции над number доступны и для double
И не допустипы операции:
n1 = o1 // т.к. object не является number
n1 = o2 // и object не является дочерним к number
Так же для переменной o1,o2 доступны любые операции присвоения,
среди указанных, а для d1,d2 доступны только операции присвоения
для типов double
Отношения операции присвоения
Можно обобщить операции присвоения и построить возможные отношения между типами данных в аспекте операции присвоения.
Рассмотри в общем:
left операция(=) right
- left - переменная некого типа Left
- right - переменная некого типа Right
| Left | Right | в коде | Отновшение left к right |
|---|---|---|---|
| Left = Right | Right = Left | Инвариантность (inv) | |
| Left - родитель к Right | Right - дочерний к Left | class Right extends Left |
left ко-вариантен к right (cov) |
| Left - дочерний к Right | Right - родитель к Left | class Left extends Right |
left контр-вариантен к right (ctr) |
| Left нет пересечений с Right | Right нет пересечений с Left | нет вариантов (no) |
- Отношения ко-вариантность и контр-вариантность - взаимно противоположны, и взаимо исключающие
- В случае если
left операция(=) rightко-варинтна, тогдаright операция(=) leftконтр-варианта - В случае если
left операция(=) rightконтр-варинтна, тогдаright операция(=) leftко-варианта
- В случае если
Расширеный пример присвоения
В разрвнх языках, где есть lambda, поддерживается переменные которые могут указывать на функции.
Для примера (Java 8+):
BiFunction<Integer,Integer,Number> f1 = (a,b) -> a+b;
BiFunction<Number,Number,Integer> f2 = ...;
Теперь код вызова
Number n1 = f1( 10, 12 )
Number n2 = f2( 10, 12 )
С точки зрения компилятора код выше возвращает один и тот же тип данных, т.к. вызов f2, хоть и возвращает Integer, но Integer является так же Number - т.е. результат вызова f2 ко-вариантен к f1
Аргументы вызова вступают в противоположное отношения:
Integer a1 = 10
Integer a2 = 12
Number n1 = f1( a1, a2 )
Number n2 = f2( a1, a2 )
- Аргменты
a1,a2инвариантны кf1 - Аргменты
a1,a2контр-варинтны кf2(Integer дочерен к Number)- Аргументы
f2имеют типNumber, который ковариантен кInteger - Ключевым моментом, с какой стороны мы смотрим на значения:
- Со стороны вызывающей - контр-вариантность
- Со стороны принимающей - ко-вариантность
- В случае вызывающей и вызваемой стороны, меняются местами отношения
- Аргументы
Сами переменные-функции f1, f2 тоже относяться в отношении:
f1ко-вариантна кf2- т.е. допустима операцияf1 = f2допустимаf2контр-вариантна кf1- т.е. допустима операцияf2 = f1не допустима