java переопределение - Перегрузка метода для нулевого аргумента




методов от (6)

Я добавил три метода с параметрами:

public static  void doSomething(Object obj) {
    System.out.println("Object called");
}

public static  void doSomething(char[] obj) {
    System.out.println("Array called");
}

public static  void doSomething(Integer obj) {
    System.out.println("Integer called");
}

Когда я doSomething(null) , компилятор выдает ошибку как неоднозначные методы . Так возникает проблема, потому что методы Integer и char[] или Integer и Object ?


Answers

Java всегда будет пытаться использовать наиболее подходящую версию доступного метода (см. JLS §15.12.2 ).

Object , char[] и Integer могут принимать значение null как допустимое значение. Поэтому все 3 версии применимы, поэтому Java придется найти наиболее конкретную.

Поскольку Object является супертипом char[] , версия массива более специфична, чем Object -version. Поэтому, если существуют только эти два метода, будет выбрана версия char[] .

Когда доступны оба варианта char[] и Integer , оба они более специфичны, чем Object но ни один не более конкретный, чем другой, поэтому Java не может решить, какой из них вызывать. В этом случае вам придется явно указать, какой из них вы хотите вызвать, отвечая аргумент соответствующему типу.

Заметим, что на практике эта проблема встречается гораздо реже, чем можно было бы подумать. Причиной этого является то, что это происходит только тогда, когда вы явно вызываете метод с null или с переменной довольно неспецифического типа (например, Object ).

Напротив, следующий вызов был бы совершенно однозначным:

char[] x = null;
doSomething(x);

Хотя вы все еще передаете значение null , Java точно знает, какой метод вызывать, поскольку он учитывает тип переменной.


есть двусмысленность из-за doSomething (char [] obj) и doSomething (Integer obj).

char [] и Integer оба одинаковы выше для null, поэтому они неоднозначны.


null - допустимое значение для любого из трех типов; поэтому компилятор не может решить, какую функцию использовать. Используйте что-то вроде doSomething((Object)null) или doSomething((Integer)null) .


Я пробовал это, и когда есть только одна пара перегруженных методов, и один из них имеет тип объекта Object, тогда компилятор всегда будет выбирать метод с более конкретным типом. Но когда существует более одного определенного типа, компилятор выдает ошибку неоднозначного метода.

Поскольку это событие времени компиляции, это может произойти только тогда, когда один намеренно передает null этому методу. Если это сделано намеренно, тогда лучше перегрузить этот метод снова без параметров или вообще создать другой метод.


Каждый класс в Java расширяет класс Object. Еще один класс Integer также расширяет Object. Следовательно, Object и Integer рассматриваются как экземпляр объекта. Поэтому, когда вы передаете значение null в качестве параметра, а компилятор путается с тем, какой метод объекта вызывается, т.е. с параметром Object или параметром Integer, поскольку оба они являются объектами, а их ссылка может быть нулевой. Но примитивы в java не расширяют Object.


Java 8 позволяет использовать методы статического интерфейса

С Java 8 интерфейсы могут иметь статические методы. Они также могут иметь конкретные методы экземпляра, но не поля экземпляров.

Здесь есть два вопроса:

  1. Почему в старые добрые времена интерфейсы не могли содержать статические методы?
  2. Почему статические методы не могут быть переопределены?

Статические методы в интерфейсах

Не было сильной технической причины, почему интерфейсы не могли иметь статические методы в предыдущих версиях. Это хорошо подведено плакатом дублирующего вопроса. Методы статического интерфейса были первоначально рассмотрены как небольшое изменение языка, а затем было официальное предложение о добавлении их в Java 7, но позже оно было отменено из-за непредвиденных осложнений.

Наконец, в Java 8 были введены методы статического интерфейса, а также методы переопределения экземпляров с реализацией по умолчанию. Тем не менее, у них все еще нет полей экземпляров. Эти функции являются частью поддержки выражения лямбда, и вы можете больше узнать о них в части H JSR 335.

Переопределение статических методов

Ответ на второй вопрос немного сложнее.

Статические методы разрешимы во время компиляции. Динамическая диспетчеризация имеет смысл, например, методы, когда компилятор не может определить конкретный тип объекта и, таким образом, не может разрешить метод для вызова. Но для вызова статического метода требуется класс, и поскольку этот класс известен статически, то для компиляции время-динамическая отправка не нужна.

Немного о том, как работают методы экземпляров, необходимо понять, что здесь происходит. Я уверен, что фактическая реализация совсем иная, но позвольте мне объяснить свое понятие отправки методов, какие модели наблюдали поведение точно.

Представьте, что каждый класс имеет хеш-таблицу, которая отображает сигнатуры методов (имя и типы параметров) на фактический кусок кода для реализации метода. Когда виртуальная машина пытается вызвать метод в экземпляре, он запрашивает объект для своего класса и просматривает запрошенную подпись в таблице класса. Если тело метода найдено, оно вызывается. В противном случае получается родительский класс класса, и поиск повторяется там. Это продолжается до тех пор, пока метод не будет найден, или нет родительских классов, что приводит к NoSuchMethodError .

Если у суперкласса и подкласса есть запись в их таблицах для одной и той же сигнатуры метода, то сначала встречается версия подкласса, а версия суперкласса никогда не используется - это «переопределение».

Теперь предположим, что мы пропустим экземпляр объекта и просто начнем с подкласса. Резолюция может выполняться, как указано выше, и дает вам своего рода «переопределяемый» статический метод. Однако разрешение может произойти во время компиляции, поскольку компилятор начинается с известного класса, а не ждет, пока среда выполнения не запросит объект неопределенного типа для своего класса. Нет смысла «переопределять» статический метод, так как всегда можно указать класс, содержащий нужную версию.

Конструктор "интерфейсы"

Вот немного больше материала, чтобы рассмотреть недавнее редактирование вопроса.

Похоже, вы хотите эффективно IXMLizable конструктор-подобный метод для каждой реализации IXMLizable . Забудьте о попытке навязать это с помощью интерфейса на минуту и ​​притворитесь, что у вас есть классы, соответствующие этому требованию. Как бы вы его использовали?

class Foo implements IXMLizable<Foo> {
  public static Foo newInstanceFromXML(Element e) { ... }
}

Foo obj = Foo.newInstanceFromXML(e);

Поскольку вы должны явно называть конкретный тип Foo при «конструировании» нового объекта, компилятор может убедиться, что он действительно имеет необходимый заводский метод. И если это не так, ну и что? Если я могу реализовать IXMLizable которому не хватает «конструктора», и я создаю экземпляр и IXMLizable его вашему коду, он является IXMLizable со всем необходимым интерфейсом.

Строительство является частью реализации, а не интерфейса. Любой код, который успешно работает с интерфейсом, не заботится о конструкторе. Любой код, который заботится о конструкторе, должен знать конкретный тип в любом случае, и интерфейс можно игнорировать.





java oop null method-overloading