Неполиморфный вызов
От: LCR Россия lj://_lcr_
Дата: 06.08.04 04:57
Оценка:
Здравствуйте. Когда-то я постил аналогичный топик, но там допустил грубую ошибку и вопрос потерял смысл — в 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()); // работает, но это уже от Лукавого... :) - объект другой


Как?
quicksort =: (($:@(<#[),(=#[),$:@(>#[)) ({~ ?@#)) ^: (1<#)
Re: Неполиморфный вызов
От: dualsoul  
Дата: 06.08.04 06:38
Оценка:
Если не нужно полиморфное поведение, то зачем его делать?
назови методы по-разному
Re: Неполиморфный вызов
От: dshe  
Дата: 06.08.04 06:39
Оценка: 2 (1)
Здравствуйте, 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>Как?


Похожий вопрос, по-моему, был здесь http://www.rsdn.ru/Forum/Message.aspx?mid=736018
Автор: Vlad_
Дата: 26.07.04


Общая идея заключается в следующем, либо делаем meth() приватной (приватные методы не виртуальные), либо статической (с добавлением явного параметра this).

Т.е.
class A {
     private 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();  // тоже выводит 10
     }
}


Либо
class A {
     public static int meth(A pthis) { return 10; }

     public void coolMeth() {
          System.out.println(meth(this)); // !!! НЕ хочется, чтобы вот здесь сработал полиморфный вызов
     }
}

class B extends A {
     public static int 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
     }
}
--
Дмитро
Re: Неполиморфный вызов
От: ykk Украина  
Дата: 06.08.04 06:42
Оценка: 2 (1)
Здравствуйте, 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());
    }

}


Такое подходит ?
Re: Неполиморфный вызов
От: yury.fedorov Италия http://esistema.it/
Дата: 06.08.04 07:13
Оценка:
Здравствуйте, LCR, Вы писали:

LCR>Как?


Я бы решил эту проблему так:

class A
{

// Changed implementation.
public int meth() { return localMeth(); }

// Added.
private final int localMeth() { return 10; }

// Changed implemenatation.
public void coolMeth()
{
System.out.println( this.localMeth() ); // !!! НЕ хочется, чтобы вот здесь сработал полиморфный вызов
}
}

Такой способ никак не влияет на структуру наследования и приемлем как в случае, когда класс А — базовый, так и когда он потомок какого-то уже ранее определенного класса.
Re[2]: Неполиморфный вызов
От: LCR Россия lj://_lcr_
Дата: 06.08.04 08:36
Оценка:
Здравствуйте, dualsoul, Вы писали:

D>Если не нужно полиморфное поведение, то зачем его делать?

D>назови методы по-разному

Не прикольно.
quicksort =: (($:@(<#[),(=#[),$:@(>#[)) ({~ ?@#)) ^: (1<#)
Re[2]: Неполиморфный вызов
От: LCR Россия lj://_lcr_
Дата: 06.08.04 08:42
Оценка:
Здравствуйте, 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 класса вверх?
quicksort =: (($:@(<#[),(=#[),$:@(>#[)) ({~ ?@#)) ^: (1<#)
Re[3]: Неполиморфный вызов
От: ykk Украина  
Дата: 06.08.04 09:00
Оценка:
Здравствуйте, LCR, Вы писали:


LCR>Хм, интересная идея. А если в super.meth() надо шагнуть на 2 класса вверх?


Тогда так уже не получится... Придется использовать reflection:

Method method = getClass().getSuperclass().getSuperclass().getMethod("meth", null);
Re[4]: Неполиморфный вызов
От: dshe  
Дата: 06.08.04 09:11
Оценка: +1
Здравствуйте, 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.java
import 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]);
    }
}


Выдает C, а не A
--
Дмитро
Re[5]: Неполиморфный вызов
От: ykk Украина  
Дата: 06.08.04 09:21
Оценка:
Здравствуйте, 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, ...) на саммом деле объект класса С. Если сделать вызов следуюющим образом

m.invoke(A.class.newInstance(), new Object[0]);


то выдаст A.
Re[6]: Неполиморфный вызов
От: dshe  
Дата: 06.08.04 09:35
Оценка: :))
Здравствуйте, 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()); // работает, но это уже от Лукавого... :) - объект другой
--
Дмитро
Re: Неполиморфный вызов
От: nant Россия  
Дата: 06.08.04 11:34
Оценка: 4 (1)
Здравствуйте, 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 может чего-то и даст, но, во-первых, не факт, во-вторых будут достаточно высокие затраты по скорости.

Если интерес академический, то ответ — "никак". Если практический, то могу об эту тему подумать. Где-нибудь во вторник.
Re[3]: Неполиморфный вызов
От: dualsoul  
Дата: 06.08.04 15:27
Оценка:
Здравствуйте, LCR, Вы писали:

LCR>Здравствуйте, dualsoul, Вы писали:


D>>Если не нужно полиморфное поведение, то зачем его делать?

D>>назови методы по-разному

LCR>Не прикольно.


тебе надо прикольно или чтобы понятно и работало?
или ты просто из академического интереса?
Re[2]: Неполиморфный вызов
От: LCR Россия lj://_lcr_
Дата: 07.08.04 05:43
Оценка:
Здравствуйте, dualsoul, Вы писали:

D>Если не нужно полиморфное поведение, то зачем его делать?

D>назови методы по-разному

На данный момент именно так и объехал — получились два метода с продублированной функциональностью.
quicksort =: (($:@(<#[),(=#[),$:@(>#[)) ({~ ?@#)) ^: (1<#)
Re[2]: Неполиморфный вызов
От: LCR Россия lj://_lcr_
Дата: 07.08.04 05:58
Оценка:
Здравствуйте, 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'а).

Изменить сигнатуру метода на (приватный, статический) не подходить, поскольку он меняет целиком метод и влияет на все точки вызова.
quicksort =: (($:@(<#[),(=#[),$:@(>#[)) ({~ ?@#)) ^: (1<#)
Re[5]: Неполиморфный вызов
От: LCR Россия lj://_lcr_
Дата: 07.08.04 06:02
Оценка:
Здравствуйте, 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]);?
quicksort =: (($:@(<#[),(=#[),$:@(>#[)) ({~ ?@#)) ^: (1<#)
Re[7]: Неполиморфный вызов
От: LCR Россия lj://_lcr_
Дата: 07.08.04 06:03
Оценка:
Здравствуйте, dshe, Вы писали:

D>Ну... тогда уж проще сделать так:

D>
LCR>>System.out.println((new A()).meth()); // работает, но это уже от Лукавого... :) - объект другой
D>


Да уж...
quicksort =: (($:@(<#[),(=#[),$:@(>#[)) ({~ ?@#)) ^: (1<#)
Re[2]: Неполиморфный вызов
От: LCR Россия lj://_lcr_
Дата: 07.08.04 06:25
Оценка:
Здравствуйте, 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();
}


То в Джаве однозначного решения как видим нет. (Не пишу "увы" потому что неразрешимых проблем связанных с этим пока не было — будем надеяться "пока").
quicksort =: (($:@(<#[),(=#[),$:@(>#[)) ({~ ?@#)) ^: (1<#)
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.