Здравствуйте. Когда-то я постил аналогичный топик, но там допустил грубую ошибку и вопрос потерял смысл — в Java все методы по умолчанию виртуальные. Но есть уже другая ситуация.
class A
{
public int meth() { return 10; }
public void coolMeth()
{
System.out.println(meth()); // !!! НЕ хочется, чтобы вот здесь сработал полиморфный вызов
}
}
class B extends A
{
public int meth() { return 20; }
}
class Test
{
public static void main(String[] args)
{
A a = new A();
B b = new B();
a.coolMeth(); // выводит 10
b.coolMeth(); // выводит 20, а я хочу 10!
}
}
Строка помеченная тремя восклицательными флагами содержит полиморфный вызов meth().
Мои попытки избавиться от этого:
System.out.println(this.meth()); // естественно не работает, поскольку динамический тип this'a = B
System.out.println((A)this.meth()); // тоже не работает, поскольку динамический тип this'a = B, а преобразование ничего не даёт.
System.out.println((new A()).meth()); // работает, но это уже от Лукавого... :) - объект другой
Здравствуйте, LCR, Вы писали:
LCR>Здравствуйте. Когда-то я постил аналогичный топик, но там допустил грубую ошибку и вопрос потерял смысл — в Java все методы по умолчанию виртуальные. Но есть уже другая ситуация.
LCR>
LCR>class A
LCR>{
LCR> public int meth() { return 10; }
LCR> public void coolMeth()
LCR> {
LCR> System.out.println(meth()); // !!! НЕ хочется, чтобы вот здесь сработал полиморфный вызов
LCR> }
LCR>}
LCR>class B extends A
LCR>{
LCR> public int meth() { return 20; }
LCR>}
LCR>class Test
LCR>{
LCR> public static void main(String[] args)
LCR> {
LCR> A a = new A();
LCR> B b = new B();
LCR> a.coolMeth(); // выводит 10
LCR> b.coolMeth(); // выводит 20, а я хочу 10!
LCR> }
LCR>}
LCR>
LCR>Строка помеченная тремя восклицательными флагами содержит полиморфный вызов meth(). LCR>Мои попытки избавиться от этого: LCR>
LCR>System.out.println(this.meth()); // естественно не работает, поскольку динамический тип this'a = B
LCR>System.out.println((A)this.meth()); // тоже не работает, поскольку динамический тип this'a = B, а преобразование ничего не даёт.
LCR>System.out.println((new A()).meth()); // работает, но это уже от Лукавого... :) - объект другой
LCR>
Общая идея заключается в следующем, либо делаем meth() приватной (приватные методы не виртуальные), либо статической (с добавлением явного параметра this).
Т.е.
class A {
privateint meth() { return 10; }
public void coolMeth() {
System.out.println(meth()); // !!! НЕ хочется, чтобы вот здесь сработал полиморфный вызов
}
}
class B extends A {
public int meth() { return 20; }
}
class Test {
public static void main(String[] args) {
A a = new A();
B b = new B();
a.coolMeth(); // выводит 10
b.coolMeth(); // тоже выводит 10
}
}
Либо
class A {
publicstaticint meth(A pthis) { return 10; }
public void coolMeth() {
System.out.println(meth(this)); // !!! НЕ хочется, чтобы вот здесь сработал полиморфный вызов
}
}
class B extends A {
publicstaticint meth(A pthis) { return 20; }
}
class Test {
public static void main(String[] args) {
A a = new A();
B b = new B();
a.coolMeth(); // выводит 10
b.coolMeth(); // тоже выводит 10
}
}
Здравствуйте, LCR, Вы писали:
LCR>Здравствуйте. Когда-то я постил аналогичный топик, но там допустил грубую ошибку и вопрос потерял смысл — в Java все методы по умолчанию виртуальные. Но есть уже другая ситуация.
LCR>
LCR>class A
LCR>{
LCR> public int meth() { return 10; }
LCR> public void coolMeth()
LCR> {
LCR> System.out.println(meth()); // !!! НЕ хочется, чтобы вот здесь сработал полиморфный вызов
LCR> }
LCR>}
LCR>class B extends A
LCR>{
LCR> public int meth() { return 20; }
LCR>}
LCR>class Test
LCR>{
LCR> public static void main(String[] args)
LCR> {
LCR> A a = new A();
LCR> B b = new B();
LCR> a.coolMeth(); // выводит 10
LCR> b.coolMeth(); // выводит 20, а я хочу 10!
LCR> }
LCR>}
LCR>
LCR>Строка помеченная тремя восклицательными флагами содержит полиморфный вызов meth(). LCR>Мои попытки избавиться от этого: LCR>
LCR>System.out.println(this.meth()); // естественно не работает, поскольку динамический тип this'a = B
LCR>System.out.println((A)this.meth()); // тоже не работает, поскольку динамический тип this'a = B, а преобразование ничего не даёт.
LCR>System.out.println((new A()).meth()); // работает, но это уже от Лукавого... :) - объект другой
LCR>
LCR>Как?
Если ты уже переопередел метод, то соответсвенно в новой сущности у тебя будет другой результат. Такие уж правила ООП.
Можно переопределить в классе B метод coolMeth, это даст требуемый результат:
class B extends A
{
public int meth() { return 20; }
public void coolMeth() {
System.out.println(super.meth());
}
}
// Changed implementation.
public int meth() { return localMeth(); }
// Added.
private final int localMeth() { return 10; }
// Changed implemenatation.
public void coolMeth()
{
System.out.println( this.localMeth() ); // !!! НЕ хочется, чтобы вот здесь сработал полиморфный вызов
}
}
Такой способ никак не влияет на структуру наследования и приемлем как в случае, когда класс А — базовый, так и когда он потомок какого-то уже ранее определенного класса.
Здравствуйте, ykk, Вы писали:
ykk>Здравствуйте, LCR, Вы писали:
LCR>>Здравствуйте. Когда-то я постил аналогичный топик, но там допустил грубую ошибку и вопрос потерял смысл — в Java все методы по умолчанию виртуальные. Но есть уже другая ситуация.
ykk>Если ты уже переопередел метод, то соответсвенно в новой сущности у тебя будет другой результат. Такие уж правила ООП. ykk>Можно переопределить в классе B метод coolMeth, это даст требуемый результат:
ykk>
ykk>class B extends A
ykk>{
ykk> public int meth() { return 20; }
ykk> public void coolMeth() {
ykk> System.out.println(super.meth());
ykk> }
ykk>}
ykk>
ykk>Такое подходит ?
Хм, интересная идея. А если в super.meth() надо шагнуть на 2 класса вверх?
Здравствуйте, ykk, Вы писали:
ykk>Здравствуйте, LCR, Вы писали:
LCR>>Хм, интересная идея. А если в super.meth() надо шагнуть на 2 класса вверх?
ykk>Тогда так уже не получится... Придется использовать reflection:
Боюсь, что сделать вызов super на два уровня по иерархии верх сделать в принципе невозможно, даже с иcпользованием reflection
class A {
public void f() {
System.out.println("A");
}
}
class B extends A {
public void f() {
System.out.println("B");
}
}
class C extends B {
public void f() {
System.out.println("C");
}
}
// Main.javaimport java.lang.reflect.Method;
public class Main {
public static void main(String[] args) throws Exception {
A a = new C();
Method m = A.class.getMethod("f", new Class[0]);
m.invoke(a, new Object[0]);
}
}
Здравствуйте, dshe, Вы писали:
D>Боюсь, что сделать вызов super на два уровня по иерархии верх сделать в принципе невозможно, даже с иcпользованием reflection
D>
D>class A {
D> public void f() {
D> System.out.println("A");
D> }
D>}
D>class B extends A {
D> public void f() {
D> System.out.println("B");
D> }
D>}
D>class C extends B {
D> public void f() {
D> System.out.println("C");
D> }
D>}
D>
D>
D>// Main.java
D>import java.lang.reflect.Method;
D>public class Main {
D> public static void main(String[] args) throws Exception {
D> A a = new C();
D> Method m = A.class.getMethod("f", new Class[0]);
D> m.invoke(a, new Object[0]);
D> }
D>}
D>
D>Выдает C, а не A
Согласен, не возможно сделать вызов без получения инстанса супер класса. То есть в данном случае ты передал в invoke(a, ...) на саммом деле объект класса С. Если сделать вызов следуюющим образом
Здравствуйте, ykk, Вы писали:
ykk>Здравствуйте, dshe, Вы писали:
ykk>Согласен, не возможно сделать вызов без получения инстанса супер класса. То есть в данном случае ты передал в invoke(a, ...) на саммом деле объект класса С. Если сделать вызов следуюющим образом
ykk>
ykk>m.invoke(A.class.newInstance(), new Object[0]);
ykk>
ykk>то выдаст A.
Ну... тогда уж проще сделать так:
LCR>System.out.println((new A()).meth()); // работает, но это уже от Лукавого... :) - объект другой
Здравствуйте, LCR, Вы писали:
LCR>Здравствуйте. Когда-то я постил аналогичный топик, но там допустил грубую ошибку и вопрос потерял смысл — в Java все методы по умолчанию виртуальные. Но есть уже другая ситуация.
<oткушено/> LCR>Как?
Из-за того, что в Java все методы виртуальные малой кровью обойти это вряд ли удастся.
Мне сейчас видятся 3 варианта:
1) То, что посоветовал ykk, только немного в другом виде:
К примеру
class A
{
public void doSomething()
{
//...
}
}
class B extends A
{
public void doSomething()
{
//...
}
public void doSomethingA()
{
super.doSomething();
}
}
class C extends A
{
public void doSomething()
{
//...
}
public void doSomethingB()
{
super.doSomething();
}
}
2) Разделение логики и данных. В этом случае количество классов увеличится в 2 раза, соответственно кода будет несколько больше и вызов будет выглядеть так
// B - наследник A, содержит только данные. Операции реализуются в другой иерархии.
// Скорее всего статически
B b = new B(...);
AOperations.doSomething(b, "param1",2, 3.0...);
Получается неявная передача this заменяется явной передачей ссылки на данные.
3) Если количество последовательных "неполиморфных" вызовов достаточно большое, то можно придумать некоторую идиому на основе шаблона Strategy и варианта 1, но в этом случае кода и "приседаний" будет больше, чем в оном первом варианте.
Reflection может чего-то и даст, но, во-первых, не факт, во-вторых будут достаточно высокие затраты по скорости.
Если интерес академический, то ответ — "никак". Если практический, то могу об эту тему подумать. Где-нибудь во вторник.
Здравствуйте, LCR, Вы писали:
LCR>Здравствуйте, dualsoul, Вы писали:
D>>Если не нужно полиморфное поведение, то зачем его делать? D>>назови методы по-разному
LCR>Не прикольно.
тебе надо прикольно или чтобы понятно и работало?
или ты просто из академического интереса?
Здравствуйте, yury.fedorov, Вы писали:
YF>Здравствуйте, LCR, Вы писали:
LCR>>Как?
YF>Я бы решил эту проблему так:
YF>class A YF>{
YF> // Changed implementation. YF> public int meth() { return localMeth(); }
YF> // Added. YF> private final int localMeth() { return 10; }
YF> // Changed implemenatation. YF> public void coolMeth() YF> { YF> System.out.println( this.localMeth() ); // !!! НЕ хочется, чтобы вот здесь сработал полиморфный вызов YF> } YF>}
YF>Такой способ никак не влияет на структуру наследования и приемлем как в случае, когда класс А — базовый, так и когда он потомок какого-то уже ранее определенного класса.
Ну естественно, данная (конкретная) задача решается рефакторингом. Но вопрос не в этом, вопрос в следующем: есть метод, есть вызов этого метода. Как в данной, конкретной точке сделать вызов этого метода неполиморфным?
Пока есть 2 решения
1. Создать новый финальный (приватный, статический) метод, дублируя (или перенося) функциональность.
2. Использовать трюк ykk, переопределяя все места, где используется данный метод meth() и где нужен неполиморфный вызов (с понятными ограничениями, которые накладывает использование super'а).
Изменить сигнатуру метода на (приватный, статический) не подходить, поскольку он меняет целиком метод и влияет на все точки вызова.
Здравствуйте, dshe, Вы писали:
D>Боюсь, что сделать вызов super на два уровня по иерархии верх сделать в принципе невозможно, даже с использованием reflection
D>
D>class A {
D> public void f() {
D> System.out.println("A");
D> }
D>}
D>class B extends A {
D> public void f() {
D> System.out.println("B");
D> }
D>}
D>class C extends B {
D> public void f() {
D> System.out.println("C");
D> }
D>}
D>
D>
D>// Main.java
D>import java.lang.reflect.Method;
D>public class Main {
D> public static void main(String[] args) throws Exception {
D> A a = new C();
D> Method m = A.class.getMethod("f", new Class[0]);
D> m.invoke(a, new Object[0]);
D> }
D>}
D>
D>Выдает C, а не A
Кхм, хотите сказать, что m.invoke(a, new Object[0]) — полиморфный вызов? А как же Method m = A.class.getMethod("f", new Class[0]);?
Здравствуйте, nant, Вы писали:
N>Из-за того, что в Java все методы виртуальные малой кровью обойти это вряд ли удастся.
N>Мне сейчас видятся 3 варианта:
N><action name="съедено" modifier=""/>
Данные грабли, которые возникли у меня, я объехал введением нового приватного метода. Посему этот вопрос уже немножко академический. Однако я не могу сказать, что он решён — всё что я понял, это то, что Джава по соображениям безопасности (или ещё каких-то) недостаточно гибка — в общем виде не позволяет сделать вызов метода независимо от динамического типа объекта.
Если в C++:
// здесь иерархия A{B{C{E,F,G},D{H,I}}}void f(A * x)
{
x->B::meth();
}
То в Джаве однозначного решения как видим нет. (Не пишу "увы" потому что неразрешимых проблем связанных с этим пока не было — будем надеяться "пока").