Правила выделения подтипов наиболее специфичных методов в Java

В §15.12.2.5 Спецификации языка Java они описывают, как Java выбирает наиболее конкретный метод для вызова из списка как доступных, так и применимых методов.

Есть одно конкретное замечание, которое я не понимаю, а именно:

(1) Тип S более конкретен, чем тип T для любого выражения, если S ‹: T

Меня больше всего беспокоит "любое выражение".

Чтобы проиллюстрировать, почему, я процитирую предыдущий абзац, в котором говорится, что, имея два метода m 1 и m 2, мы можем сказать m 1 более конкретен, чем m 2 для вызова с выражениями аргументов e 1, ..., e k если:

m 2 не является универсальным, а m 1 и m 2 применимы при строгом или свободном вызове, и где m 1 имеет формальные типы параметров S 1, ..., S n и m 2 имеет формальные типы параметров T 1 , ..., T n, тип S i более специфичен, чем T i для аргумента e i для всех i (1 ≤ i ≤ n, n = k).

Итак, если тип S i (m 1) более конкретен, чем T i (m 2) с аргументом выражение e i для всех i, тогда m 1 более конкретное, чем m 2

Теперь рассмотрим это:

int a(Number a) { return 1; } // m1
int a(Double a) { return 2; } // m2

Из Double <: Number следует, что для любого выражения Double более специфично, чем Number (из цитаты 1).

У нас есть:

m 1: S 1 => Число

м 2: T 1 => Двойной

Но тогда, если наше выражение аргумента аргумента e 1 имеет тип Number m 2, это наиболее специфичный метод, поскольку Double наиболее специфичен для любого выражения ( цитата 1).

Однако, если мы передадим Number в a(), результат будет 1, т.е. метод, принимающий Double, не выбран. Но в цитате 1 говорится, что если Double является подтипом Number, то любое выражение Double будет более конкретным, так почему же оно выбирает a(Number)?

Что мне здесь не хватает?

Примечание: я знаю, что Number не может быть назначен Double, и компилятору не имеет смысла выбирать другой, я просто пытаюсь понять, что я неправильно интерпретирую с первого раза. Цитировать.


person ᴘᴀɴᴀʏɪᴏᴛɪs    schedule 22.12.2016    source источник
comment
Это меня смущает. Вы уже процитировали и применимо, а затем задаетесь вопросом, почему метод с неприменимым типом параметра не выбран, хотя он имеет более конкретный тип и был бы использован , если применимо? Какие? a(Double) не используется, если вы проходите Number, здесь больше не о чем рассказывать.   -  person Tom    schedule 23.12.2016
comment
@Tom Боже мой. Я прошел через все трудности, пытаясь понять это, когда основная посылка была ложной. Я все время считал, что это применимо. Большое спасибо!   -  person ᴘᴀɴᴀʏɪᴏᴛɪs    schedule 23.12.2016
comment
Вы можете переосмыслить эти правила с другими типами, такими как Double, Integer и null (просто черт возьми: D). И, может быть, примитивы тоже. Это может быть интересно.   -  person Tom    schedule 23.12.2016
comment
ИМХО более важно то, что решение принимается во время компиляции, а не во время выполнения.   -  person Timothy Truckle    schedule 23.12.2016
comment
@Tom Да, я просто неверно истолковал расширение ссылок и предположил, что это применимо только потому, что они сидели на одном дереве наследования   -  person ᴘᴀɴᴀʏɪᴏᴛɪs    schedule 23.12.2016


Ответы (1)


Оказывается, мое недоразумение было связано не с цитатой, а с тем, какая применимая функция будет находиться при вызове свободным или строгим.

В частности, контексты вызова строгий / свободный допускают набор преобразований.

  • Преобразование идентичности, т.е. От Number до Number
  • Расширяющееся примитивное преобразование, т.е. byte to short,int,long,float or double
  • Расширяющееся преобразование ссылок, т.е. От Double до Number
  • Конверсия бокса, т.е. int to Integer (необязательно) с последующим расширением ссылочного преобразования
  • Преобразование распаковки, т.е. Integer to int необязательно с последующим расширяющимся примитивным преобразованием

В моем примере я упустил из виду тот факт, что расширяющееся преобразование ссылки нельзя применить к Number, потому что Number не является подтипом Double, поэтому int a(Double a) не считается применимым методом.

person ᴘᴀɴᴀʏɪᴏᴛɪs    schedule 22.12.2016