Частичное переопределение в Java (или динамическое переопределение при перегрузке) - PullRequest
2 голосов
/ 25 апреля 2010

Если у меня есть родитель-потомок, который определяет какой-то метод .foo () следующим образом:

class Parent {
  public void foo(Parent arg) {
    System.out.println("foo in Function");
  }
}
class Child extends Parent {
  public void foo(Child arg) {
    System.out.println("foo in ChildFunction");
  }  
}

Когда я назвал их так:

  Child f = new Child();
  Parent g = f;
  f.foo(new Parent());
  f.foo(new Child());  
  g.foo(new Parent());
  g.foo(new Child());

вывод:

foo in Parent
foo in Child
foo in Parent
foo in Parent

Но я хочу этот вывод:

foo in Parent
foo in Child
foo in Parent
foo in Child

У меня есть дочерний класс, который расширяет родительский класс. В классе Child я хочу "частично переопределить" foo() Родителя, то есть, если тип аргумента arg - Child, тогда вызывается Child * foo() вместо foo().

Родителя.

Это работает нормально, когда я позвонил f.foo(...) как ребенок; но если я ссылаюсь на него из псевдонима Родителя, например, в g.foo(...), тогда Родитель foo(..) будет вызываться независимо от типа arg.

Насколько я понимаю, то, что я ожидаю, не произойдет, потому что перегрузка метода в Java является ранним связыванием (то есть разрешается статически во время компиляции), в то время как переопределение метода является поздним связыванием (то есть разрешается динамически во время компиляции), и так как я Определяя функцию с технически другим типом аргумента, я технически перегружаю определение класса Parent определением, а не переопределяю его. Но то, что я хочу сделать, это концептуально «частично переопределить», когда аргумент foo() является подклассом аргумента foo() родителя.

Я знаю, что могу определить переопределение сегмента foo(Parent arg) в Child, которое проверяет, является ли фактический тип arg Parent или Child, и передает его правильно, но если у меня есть двадцать Child, это будет большим количеством дублирования небезопасного кода.

В моем реальном коде Parent - это абстрактный класс с именем «Function», который просто выбрасывает NotImplementedException(). Дочерние элементы включают в себя «Полином», «Логарифмический» и т. Д., А .foo () включает в себя такие вещи, как Child.add (Child), Child.intersectionsWith (Child) и т. Д. Не все комбинации Child.foo (OtherChild) являются разрешимыми и На самом деле даже не все Child.foo (Child) разрешимы. Поэтому лучше всего определить все неопределенное (то есть, выбрав NotImplementedException), а затем определить только те, которые могут быть определены.

Так что вопрос: есть ли способ переопределить только часть родительского foo ()? Или есть лучший способ сделать то, что я хочу?

РЕДАКТИРОВАТЬ :

@ Zeiss: если я использую Double Dispatch, вот так:

class Parent {
  public void foo(Parent arg) {
    System.out.println("foo in Parent");
  }
}
class Child extends Parent {
  public void foo(Parent arg) {
    System.out.println("foo in Child(Parent)");
    arg.foo(this);
  }
  public void foo(Child arg) {
    System.out.println("foo in Child(Child)");
  }
}

Я получил бесконечную рекурсию:

(stack): 
StackOverflowError: ...
...
    at sketch_apr25a$Child.foo(sketch_apr25a.java:35)
...
(output):
...
foo in Child(Parent)
...

при выполнении g.foo(new Child());. В остальном все в порядке, так как вывод:

foo in Child(Parent)
foo in Parent

foo in Child(Child)

foo in Child(Parent)
foo in Parent

foo in Child(Parent)
(infinite recursion follows)

Почему это происходит? g - псевдоним Родителя, но он обращается к дочернему элементу foo (Родителю)?

Ответы [ 2 ]

3 голосов
/ 25 апреля 2010

Разве это не вариант использования для Двойной отправки ?


Обновление:

class Function {
    public void add(Function f) {
        f.addMe(this);
    }

    public void addMe(Function f) {
        // Default case
        throw NotImplementedException();
    }
    public void addMe(Logarithmic log) {
        // Code that handles the real
    }
}

class Logarithmic extends Function {
    // This object supports adding
    public void add(Function f) {
        f.addMe(this);
    }
}


Logarithmic log = new Logarithmic();
log.add(new Function()); 
log.add(new Logarithmic()); 

Function f = log;
f.add(new Function()); 
f.add(new Logarithmic()); 
0 голосов
/ 25 апреля 2010

Я заставил его работать, явно переопределив foo(Parent arg) в Child, как это -

class Parent {
    public void foo(Parent arg) {
        System.out.println("foo in Parent");
    }
}

class Child extends Parent {
    @Override
    public void foo(Parent arg) {
        System.out.println("foo in Child(Parent)");
        if (arg instanceof Child) {
            foo((Child)arg);
        } else {
            super.foo(arg);
        }
    }
    public void foo(Child arg) {
        System.out.println("foo in Child(Child)");
    }
}

Это похоже на логику

Я хочу «частично переопределить» родительскую функцию foo (), то есть, если аргумент аргумента имеет тип Child, тогда метод child (foo () вызывается вместо родительской функции foo ().

Но вместо того, чтобы "частично переопределить" метод, вам действительно нужно переопределить метод.

...