2010-04-09 5 views
3
class A   { void F() { System.out.println("a"); }} 
class B extends A { void F() { System.out.println("b"); }} 

public class X { 
    public static void main(String[] args) { 
     A objA = new B(); 
     objA.F(); 
    } 
} 

Здесь F() вызывается динамически, не так ли?Поддерживает ли Java динамический вызов метода?

This article говорит:

... байт-код Java не поддерживает динамического вызова метода. Есть три поддерживаемых режима вызовов: invokestatic, invokespecial, invokeinterface или invokevirtual. Эти режимы позволяют вызывать методы с известной подписью. Мы говорим о строго типизированном языке. Это позволяет совершать некоторые проверки непосредственно при времени компиляции.

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

Что мне не хватает?

+0

Что происходит, когда вы запускаете программу? –

+0

@Dave: prints 'b' – Lazer

+1

Автору этой статьи нужен урок при подсчете. – Syntactic

ответ

13

Вы путаете динамический вызов с динамического связывания ..

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

Что это значит?

Это означает, что в вашем примере Java вызовет реализацию на объекте B, потому что тип времени выполнения переменной objA равен B; и он будет компилироваться, потому что он знает, что BявляетсяA, поэтому вызов метода не будет работать во время выполнения (objA будет иметь реализацию F).

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

Только для мелочей: функция invokedynamic будет добавлена ​​с Java7, потому что многие языки сценариев были написаны для работы поверх JVM, а отсутствие динамической функции вызова заставили разработчиков этих языков добавить средний слой между сценарий и реальный JVM, который заботится о динамическом вызове, используя отражение. Конечно, этот подход вызывает много накладных расходов (подумайте о Grovvy's MetaClass), поэтому Sun решила оказать им помощь.

+0

+1 для мелочей –

1

В вашем примере вызывается правильный метод, поскольку полиморфно экземпляр B отображается как экземпляр A. Метод может быть найден путем изучения типа времени выполнения объекта; т.е. B; в отличие от типа времени компиляции ссылки на объект, А.Другая важная часть - это подпись метода - они всегда должны совпадать (полиморфно, конечно).

Это отличается от динамических языков, потому что в них практически нет времени компиляции для объекта - и все должно быть разрешено во время выполнения.

1

Фактически, вам не хватает того, что это часть «invokevirtual», которая объясняется в статье.

Вы просто переопределяете метод и используете таблицу виртуальных методов для вызова правильного метода.

0

Я бы не назвал ваш пример «динамический», а не виртуальный. Потому что во время компиляции известно имя метода и подпись (и его существование проверяется компилятором). Единственное, что разрешено во время выполнения, - это конкретная реализация, которая будет использоваться для этого метода.

Более подходящий пример вызова «динамического» метода будет включать отражение (см. Класс Method). Таким образом, методы, существование которых неизвестно при компиляции, могут быть вызваны во время выполнения (это широко используется каркасами, а не только кодом приложения).

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

0

Вы можете создавать функциональные интерфейсы.

class Logger { 
    private BiConsumer<Object, Integer> logger = null; 

    // ... 

    private Logger(Object logger) { 
     this.akkaLogger = (LoggingAdapter) logger; 
     this.logger = (message, level) -> { 
      switch (level) { 
       case INFO: akkaInfo(message); 
          break; 
       case DEBUG: akkaDebug(message); 
          break; 
       case ERROR: akkaError(message); 
          break; 
       case WARN: akkaWarn(message); 
          break; 
      } 
     }; 
    } 

    private Logger() { 
     this.logger = (message, level) -> System.out.println(message); 
    } 

    // ... 
} 
Смежные вопросы