Elettracompany.com

Компьютерный справочник
3 просмотров
Рейтинг статьи
1 звезда2 звезды3 звезды4 звезды5 звезд
Загрузка...

Виртуальные функции java

Можете ли вы написать виртуальные функции / методы на Java?

можно писать виртуальный методы в Java, как это было бы в C++?

или, есть ли правильный подход Java, который вы можете реализовать, который производит подобное поведение? Могу я привести несколько примеров?

6 ответов

с Википедия

на Java, все нестатические методы по по умолчанию «виртуальные функции.» только методы, отмеченные ключевое слово final, который нельзя отменить, вместе с частные методы, которые не в наследство, являются non-virtual.

можете ли вы писать виртуальные функции на Java?

да. Фактически, все методы экземпляра в Java являются виртуальными по умолчанию. Только некоторые методы не являются виртуальными:

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

вот несколько примеров:

«нормальные» виртуальные функции

следующий пример из старая версия страницы Википедии, упомянутой в другом ответе.

пример с интерфейсами

Java методы интерфейса все виртуальный. Они!—33—>должны быть виртуальными, потому что они полагаются на классы реализации для предоставления реализаций метода. Код для выполнения будет выбран только во время выполнения.

пример с виртуальными функциями с абстрактными классами.

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

все функции в Java являются виртуальными по умолчанию.

вы должны сделать все возможное, чтобы написать не виртуальные функции, добавив ключевое слово «final».

Это противоположность C++/C# по умолчанию. По умолчанию функции класса не являются виртуальными; вы делаете их так, добавляя модификатор «virtual».

все non-private методы экземпляра являются виртуальными по умолчанию в Java.

В C++, частные методы могут быть виртуальными. Это можно использовать для идиомы non-virtual-interface (NVI). В Java вам нужно будет защитить переопределяемые методы NVI.

из спецификации языка Java, v3:

8.4.8.1 переопределение (методами экземпляра) метода экземпляра m1 объявленные в классе C может другой метод экземпляра, м2, объявленный в классе A iff все следующие верны:

  1. C является подклассом А.
  2. подпись M1 подподписью (§8.4.2) подписи м2.
  3. либо * m2 является общедоступным, защищенным или объявленным с доступом по умолчанию в пакет C или * m1 переопределяет метод m3, M3, отличный от M1, m3, отличный от m2, таким образом, что m3 переопределяет m2.

Да, вы можете писать виртуальные «функции» на Java.

Вызов виртуального метода в конструкторе: разница между Java и С++

14 Xiao Jia [2012-11-18 16:07:00]

Это приведет к выводу

Однако в С++ результат отличается:

Что вызывает такую ​​разницу между Java и С++? Это время, когда vtable инициализируется?

EDIT: Я понимаю механизмы Java и С++. То, что я хочу знать, — это понимание этого дизайнерского решения.

java c++ virtual-functions object-lifetime vptr

7 ответов

13 Решение Konrad Rudolph [2012-11-18 16:24:00]

Оба подхода явно имеют недостатки:

  • В Java вызов переходит к методу, который не может правильно использовать this , потому что его элементы havent еще не инициализированы.
  • В С++ вызывается неинтуитивный метод (т.е. не тот из производного класса), если вы не знаете, как С++ строит классы.

Почему каждый язык делает то, что он делает, является открытым вопросом, но оба, вероятно, утверждают, что это «безопасный» вариант: способ С++ предотвращает использование неинициализированных членов; Подход Javas позволяет полиморфную семантику (в некоторой степени) внутри конструктора класса (который является вполне допустимым прецедентом).

Ну, вы уже связались с обсуждение вопросов, но это в основном проблемно-ориентированное, не вникающее в обоснование, почему.

Короче говоря, это для безопасности типа.

Это один из немногих случаев, когда С++ превосходит Java и С# по типу безопасности.; -)

Когда вы создаете класс A , в С++ вы можете позволить каждому конструктору A инициализировать новый экземпляр так, чтобы выполнялись все общие предположения о его состоянии, называемые инвариантом класса. Например, частью инварианта класса может быть то, что элемент указателя указывает на некоторую динамически распределенную память. Когда каждый общедоступный метод сохраняет инвариант класса, то он также должен удерживать и при входе в каждый метод, что значительно упрощает вещи – по крайней мере, для хорошо подобранного инварианта класса!

В каждом методе не требуется никакой дополнительной проверки.

В отличие от этого, используя двухфазную инициализацию, например, в Microsoft MFC и ATL-библиотеках, вы никогда не можете быть уверены, все ли было правильно инициализировано при вызове метода (нестатической функции-члена). Это очень похоже на Java и С#, за исключением того, что на этих языках отсутствие инвариантов класса инвариантов исходит от этих языков, но только не позволяет активно поддерживать концепцию инварианта класса. Короче говоря, виртуальные методы Java и С#, вызываемые из конструктора базового класса, могут быть вызваны на производный экземпляр, который еще не был инициализирован, где инвариант (производный) класса еще не установлен!

Итак, эта поддержка языка С++ для инвариантов классов действительно велика, помогая избавиться от множества проверок и много разочаровывающих запутанных ошибок.

Тем не менее, немного сложно выполнить инициализацию производного класса в конструкторе базового класса, например. делать общие вещи в самом верхнем GUI Widget классе & rsquo; конструктор.

Для более полного рассмотрения наиболее распространенного случая см. также мою статью в блоге «Как избежать пост-строительства с помощью заводов-изготовителей» .

Независимо от того, как это реализовано, разница в том, что говорит определение языка. Java позволяет вам вызывать функции на производном объекте, который не был полностью инициализирован (он был инициализирован нулем, но его конструктор не запускался). С++ не позволяет этого; пока не будет запущен конструктор производного класса, нет производного класса.

2 je4d [2012-11-18 17:28:00]

Надеюсь, это поможет:

Когда ваша строка new Derived() выполняется, первое, что происходит, это выделение памяти. Программа выделит кусок памяти достаточно большой, чтобы удерживать оба члена Base и Derrived . На данный момент нет объекта. Это просто неинициализированная память.

Читать еще:  Игры на javascript

Когда конструктор Base завершен, память будет содержать объект типа Base , а инвариант класса для Base должен выполняться. В этой памяти до сих пор нет объекта Derived .

Во время построения базы объект Base находится в частично построенном состоянии, но правила языка доверяют вам достаточно, чтобы вы могли называть свои собственные функции-члены на частично построенном объекте. Объект Derived не создан частично. Он не существует.

Ваш вызов виртуальной функции заканчивается вызовом версии базового класса, поскольку в этот момент времени Base является наиболее производным типом объекта. Если бы он вызывал Derived::virt , он вызывал бы функцию-член Derived с помощью этого указателя, который не имеет типа Derrived , безопасность разломов.

Логически, класс — это то, что создается, имеет функции, вызываемые на нем, а затем уничтожается. Вы не можете вызывать функции-члены для объекта, который не был создан, и вы не можете вызвать функции-члены на объекте после его уничтожения. Это довольно фундаментально для ООП, правила языка С++ просто помогают вам избегать действий, нарушающих эту модель.

0 ChrisW [2012-11-18 16:22:00]

На самом деле, я хочу знать, что такое понимание этого дизайнерского решения.

Возможно, что в Java каждый тип выводится из Object, каждый Object — это какой-то тип листа, а также один JVM, в котором все объекты построены.

В С++ многие типы не являются виртуальными вообще. Кроме того, в С++ базовый класс и подкласс могут быть скомпилированы для машинного кода отдельно: так что базовый класс делает то, что он делает, вне зависимости от того, является ли он суперклассом чего-то другого.

0 kosa [2012-11-18 16:11:00]

В Java вызов метода основан на типе объекта, поэтому он ведет себя так (я мало знаю о С++).

Здесь ваш объект имеет тип Derived , поэтому jvm вызывает метод на объекте Derived .

Если понимать виртуальную концепцию четко, эквивалент в java является абстрактным, ваш код прямо сейчас не является действительно виртуальным кодом в java-терминах.

Счастлив обновить мой ответ, если что-то не так.

0 KGhatak [2016-09-22 11:06:00]

Конструкторы не полиморфны как для языков С++, так и для Java, тогда как метод может быть полиморфным на обоих языках. Это означает, что когда внутри конструктора появляется полиморфный метод, дизайнеры будут иметь два варианта.

  • Либо строго соответствуют семантике на неполиморфных конструктор и, следовательно, рассмотрим любой полиморфный метод, вызываемый внутри конструктор как неполиморфный. Вот как С++ делает § .
  • Или, компромисс строгая семантика неполиморфного конструктора и придерживаться строгая семантика полиморфного метода. Таким образом, полиморфные методы из конструкторов всегда полиморфны. Так работает Java.

Поскольку ни одна из стратегий не предлагает или не компрометирует какие-либо реальные преимущества по сравнению с другими, но все же Java-способ делает это, уменьшает количество накладных расходов (нет необходимости дифференцировать полиморфизм на основе контекста конструкторов), а так как Java был разработан после С++, Я бы предположил, что разработчик Java выбрал второй вариант, видя преимущество меньших накладных расходов.

§ . Пусть метод вызывается внутри конструктора как неполиморфный. Вот как С++ делает « может быть запутанным без тщательного изучения контекста, я добавляю формализацию, чтобы точно определить, что я имел в виду.

Если класс C имеет прямое определение некоторой виртуальной функции F , и ее ctor имеет вызов в F , то любое (косвенное) обращение C s ctor на экземпляр дочернего класса T не будет влияют на выбор F ; и на самом деле C::F всегда будет вызываться из C s ctor. В этом смысле вызов виртуального F является менее полиморфным (по сравнению, например, Java, который выберет F на основе T)
Кроме того, важно отметить, что если C наследует определение F от некоторого родителя P и не переопределяет F , тогда C s ctor будет вызывать P::F и даже это, IMHO, может быть определено статически.

А в Java есть виртуальные функции?

Я тут бегло почитал инет на тему Java for C++ programmers и так и не понял, есть ли в в Java виртуальные функции. Может пропустил?

Re: А в Java есть виртуальные функции?

Re: А в Java есть виртуальные функции?

А у меня другой вопрос по java: стоит ли повсеместно использовать final для аргументов метода, которые не должны изменяться? В C++, например, рекомендуют использовать const, а в стандартной библилотеке java я этого не заметил.

Re: А в Java есть виртуальные функции?

>> стоит ли повсеместно использовать final для аргументов метода

Если компилятор не требует, то имхо нафиг не нужно.

Re: А в Java есть виртуальные функции?

>> есть ли в в Java виртуальные функции

Кхм. Есть. Они там все виртуальные из коробки.

Re: А в Java есть виртуальные функции?

> А у меня другой вопрос по java: стоит ли повсеместно использовать final для аргументов метода, которые не должны изменяться? В C++, например, рекомендуют использовать const, а в стандартной библилотеке java я этого не заметил.

В C++ имеет смысл объявлять аргумет как const T *arg или const T &arg. Но нет смысла писать const T arg: здесь const ничего не меняет. В яве такого обилия вариантов нет. Фактически там аргументы передаются как T &arg, но без возможности сделать arg=boo. Поэтому имеет смысл писать final SomeClass arg, но писать final int arg смысла нет.

Re: А в Java есть виртуальные функции?

>> стоит ли повсеместно использовать final для аргументов метода

> Если компилятор не требует, то имхо нафиг не нужно.

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

Читать еще:  Java lang illegalstateexception что это

Re: А в Java есть виртуальные функции?

>Поэтому имеет смысл писать final SomeClass arg.

Много где написано что имеет, но javap упорно не торопиться подтвердить сказанное. Может натыкались на обратное?

Re: А в Java есть виртуальные функции?

Разобрался. В плюсах, для const-аргументов, которые являются объектами, нельзя вызывать non-const методы. А в java, действительно, только прямое присваивание запрещается.

И, для сравнения:
c++ std::vector: void push_back ( const T& x )
java.util.ArrayList: public boolean add(E e)

Видимо, final не нужен.

Re: А в Java есть виртуальные функции?

А где написано? В «Thinking in Java» (Bruce Eckel) очень поверхностно final-аргументы рассматриваются.

Re: А в Java есть виртуальные функции?

>> при вызове метода компилятор может рассчитывать, что объект-параметр не изменится

Единственное, на что может рассчитывать компилятор в этом случае, это то, что не будет param = new SomeClass(), но ни как ни то, что объект не изменит свое состояние. В чем тут профит оптимизации пока не понятно.

Re: А в Java есть виртуальные функции?

>А у меня другой вопрос по java: стоит ли повсеместно использовать final для аргументов метода, которые не должны изменяться?

Теоретически final — аргументы можно выгоднее заJIT’ить, но практически вряд ли. Хотя Джошуа Блох и говорил где-то что это хорошая подсказка для HotSpot-а сами сановцы в своем JDK используют final только там где без него не компилируется, то есть для доступа к локальным переменным охватывающей функции из анонимного класса.

Указатели и виртуальные функции в Java

В настоящее время в Интернете можно найти множество статей как о перспективности платформы Java, так и об её ограниченности. Многих программистов, только присматривающихся к Яве, могут отпугнуть частые заявления, типа: «низкое быстродействие», «отсутствие указателей» и т.д.

На быстродействии я останавливаться не буду. По этой теме можно найти множество статей, например эту. В этой статье я попытаюсь развеять мнение, что в Java отсутствуют указатели. Да, они там действительно отсутствуют в том виде, в котором их можно увидеть в C/C++. Но не даром же все сложные типы в Java называются ссылочными. Можно рассмотреть пример на C. Эта программа меняет местами значения переменных x и y. Могут быть и другие ситуации, где результат должен возвращаться через параметры функции. Но это самый простейший, и, по-моему, самый доходчивый пример.

Результат работы программы будет выглядеть следующим образом:

В Java с простыми типами нельзя сделать это же самое. Но кто нам мешает написать класс-оболочку для простого типа? В общем, рассмотрим аналогичный пример на Java.

Сначала опишем класс-оболочку для типа int:

public class IntPtr <
public int value;

public IntPtr ( int a ) <
value = a;
>
>

Затем, используем этот класс в программе:

public class Main <
public void change ( IntPtr x, IntPtr y ) <
int z = x.value;
x.value = y.value;
y.value = z;
>

public static void main ( String [] args ) <
Main m = new Main () ;
IntPtr a = new IntPtr ( 2 ) ;
IntPtr b = new IntPtr ( 3 ) ;
System.out.print ( a.value ) ;
System.out.print ( ‘ ‘ ) ;
System.out.println ( b.value ) ;
m.change ( a, b ) ;
System.out.print ( a.value ) ;
System.out.print ( ‘ ‘ ) ;
System.out.println ( b.value ) ;
>
>

Результат будет таким же:

Недостатком использования такого вида «указателей» может быть только «лишняя писанина», которой нет в С. Но при наличии таких сред, как NetBeans или Eclipse (и некоторых коммерческих), с их развитыми суфлёрами кода, я думаю, наличие «лишнего» кода не является серьёзной проблемой.

Далее можно отметить наличие в Яве только виртуальных функций. При этом не нужно писать перед функцией слово virtual. Да его и нет в языке. Рассмотрим пример:

package virtualfunctest;
//суперкласс
public class Parent <
protected void f1 () <
System.out.println ( «Parent class» ) ;
>
public void f2 () <
f1 () ;
>
>

package virtualfunctest;
// производный класс
public class Child extends Parent <
protected void f1 () <
System.out.println ( «Child class» ) ;
>
>

// использование функций
package virtualfunctest;

public class Main <
public static void main ( String [] args ) <
Parent obj;
obj= new Parent () ;
obj.f2 () ;
obj= new Child () ;
obj.f2 () ;
>
>

Таким образом, можно увидеть, что язык Java всё же имеет указатели (об этом, кстати, разработчики из Sun Microsystems неоднократно говорили). Главное – это подход к ним. Я не думаю, что в этой статье я показал что-то новое людям, уже работавшим с этим языком. Но думаю, что для новичков эта статься будет хоть чем-то полезна.

А вообще, всё, чего не хватает в языке Ява можно реализовать с помощью JNI. Хотя в данный момент его вполне можно назвать самодостаточным языком.

Вызов виртуального метода в конструкторе: разница между Java и С++

14 Xiao Jia [2012-11-18 16:07:00]

Это приведет к выводу

Однако в С++ результат отличается:

Что вызывает такую ​​разницу между Java и С++? Это время, когда vtable инициализируется?

EDIT: Я понимаю механизмы Java и С++. То, что я хочу знать, — это понимание этого дизайнерского решения.

java c++ virtual-functions object-lifetime vptr

7 ответов

13 Решение Konrad Rudolph [2012-11-18 16:24:00]

Оба подхода явно имеют недостатки:

  • В Java вызов переходит к методу, который не может правильно использовать this , потому что его элементы havent еще не инициализированы.
  • В С++ вызывается неинтуитивный метод (т.е. не тот из производного класса), если вы не знаете, как С++ строит классы.

Почему каждый язык делает то, что он делает, является открытым вопросом, но оба, вероятно, утверждают, что это «безопасный» вариант: способ С++ предотвращает использование неинициализированных членов; Подход Javas позволяет полиморфную семантику (в некоторой степени) внутри конструктора класса (который является вполне допустимым прецедентом).

Ну, вы уже связались с обсуждение вопросов, но это в основном проблемно-ориентированное, не вникающее в обоснование, почему.

Короче говоря, это для безопасности типа.

Это один из немногих случаев, когда С++ превосходит Java и С# по типу безопасности.; -)

Когда вы создаете класс A , в С++ вы можете позволить каждому конструктору A инициализировать новый экземпляр так, чтобы выполнялись все общие предположения о его состоянии, называемые инвариантом класса. Например, частью инварианта класса может быть то, что элемент указателя указывает на некоторую динамически распределенную память. Когда каждый общедоступный метод сохраняет инвариант класса, то он также должен удерживать и при входе в каждый метод, что значительно упрощает вещи – по крайней мере, для хорошо подобранного инварианта класса!

Читать еще:  Java nio charset

В каждом методе не требуется никакой дополнительной проверки.

В отличие от этого, используя двухфазную инициализацию, например, в Microsoft MFC и ATL-библиотеках, вы никогда не можете быть уверены, все ли было правильно инициализировано при вызове метода (нестатической функции-члена). Это очень похоже на Java и С#, за исключением того, что на этих языках отсутствие инвариантов класса инвариантов исходит от этих языков, но только не позволяет активно поддерживать концепцию инварианта класса. Короче говоря, виртуальные методы Java и С#, вызываемые из конструктора базового класса, могут быть вызваны на производный экземпляр, который еще не был инициализирован, где инвариант (производный) класса еще не установлен!

Итак, эта поддержка языка С++ для инвариантов классов действительно велика, помогая избавиться от множества проверок и много разочаровывающих запутанных ошибок.

Тем не менее, немного сложно выполнить инициализацию производного класса в конструкторе базового класса, например. делать общие вещи в самом верхнем GUI Widget классе & rsquo; конструктор.

Для более полного рассмотрения наиболее распространенного случая см. также мою статью в блоге «Как избежать пост-строительства с помощью заводов-изготовителей» .

Независимо от того, как это реализовано, разница в том, что говорит определение языка. Java позволяет вам вызывать функции на производном объекте, который не был полностью инициализирован (он был инициализирован нулем, но его конструктор не запускался). С++ не позволяет этого; пока не будет запущен конструктор производного класса, нет производного класса.

2 je4d [2012-11-18 17:28:00]

Надеюсь, это поможет:

Когда ваша строка new Derived() выполняется, первое, что происходит, это выделение памяти. Программа выделит кусок памяти достаточно большой, чтобы удерживать оба члена Base и Derrived . На данный момент нет объекта. Это просто неинициализированная память.

Когда конструктор Base завершен, память будет содержать объект типа Base , а инвариант класса для Base должен выполняться. В этой памяти до сих пор нет объекта Derived .

Во время построения базы объект Base находится в частично построенном состоянии, но правила языка доверяют вам достаточно, чтобы вы могли называть свои собственные функции-члены на частично построенном объекте. Объект Derived не создан частично. Он не существует.

Ваш вызов виртуальной функции заканчивается вызовом версии базового класса, поскольку в этот момент времени Base является наиболее производным типом объекта. Если бы он вызывал Derived::virt , он вызывал бы функцию-член Derived с помощью этого указателя, который не имеет типа Derrived , безопасность разломов.

Логически, класс — это то, что создается, имеет функции, вызываемые на нем, а затем уничтожается. Вы не можете вызывать функции-члены для объекта, который не был создан, и вы не можете вызвать функции-члены на объекте после его уничтожения. Это довольно фундаментально для ООП, правила языка С++ просто помогают вам избегать действий, нарушающих эту модель.

0 ChrisW [2012-11-18 16:22:00]

На самом деле, я хочу знать, что такое понимание этого дизайнерского решения.

Возможно, что в Java каждый тип выводится из Object, каждый Object — это какой-то тип листа, а также один JVM, в котором все объекты построены.

В С++ многие типы не являются виртуальными вообще. Кроме того, в С++ базовый класс и подкласс могут быть скомпилированы для машинного кода отдельно: так что базовый класс делает то, что он делает, вне зависимости от того, является ли он суперклассом чего-то другого.

0 kosa [2012-11-18 16:11:00]

В Java вызов метода основан на типе объекта, поэтому он ведет себя так (я мало знаю о С++).

Здесь ваш объект имеет тип Derived , поэтому jvm вызывает метод на объекте Derived .

Если понимать виртуальную концепцию четко, эквивалент в java является абстрактным, ваш код прямо сейчас не является действительно виртуальным кодом в java-терминах.

Счастлив обновить мой ответ, если что-то не так.

0 KGhatak [2016-09-22 11:06:00]

Конструкторы не полиморфны как для языков С++, так и для Java, тогда как метод может быть полиморфным на обоих языках. Это означает, что когда внутри конструктора появляется полиморфный метод, дизайнеры будут иметь два варианта.

  • Либо строго соответствуют семантике на неполиморфных конструктор и, следовательно, рассмотрим любой полиморфный метод, вызываемый внутри конструктор как неполиморфный. Вот как С++ делает § .
  • Или, компромисс строгая семантика неполиморфного конструктора и придерживаться строгая семантика полиморфного метода. Таким образом, полиморфные методы из конструкторов всегда полиморфны. Так работает Java.

Поскольку ни одна из стратегий не предлагает или не компрометирует какие-либо реальные преимущества по сравнению с другими, но все же Java-способ делает это, уменьшает количество накладных расходов (нет необходимости дифференцировать полиморфизм на основе контекста конструкторов), а так как Java был разработан после С++, Я бы предположил, что разработчик Java выбрал второй вариант, видя преимущество меньших накладных расходов.

§ . Пусть метод вызывается внутри конструктора как неполиморфный. Вот как С++ делает « может быть запутанным без тщательного изучения контекста, я добавляю формализацию, чтобы точно определить, что я имел в виду.

Если класс C имеет прямое определение некоторой виртуальной функции F , и ее ctor имеет вызов в F , то любое (косвенное) обращение C s ctor на экземпляр дочернего класса T не будет влияют на выбор F ; и на самом деле C::F всегда будет вызываться из C s ctor. В этом смысле вызов виртуального F является менее полиморфным (по сравнению, например, Java, который выберет F на основе T)
Кроме того, важно отметить, что если C наследует определение F от некоторого родителя P и не переопределяет F , тогда C s ctor будет вызывать P::F и даже это, IMHO, может быть определено статически.

Ссылка на основную публикацию
ВсеИнструменты 220 Вольт
Adblock
detector
×
×