Передача суперкласса в качестве параметра методу, ожидающему подкласс - PullRequest
4 голосов
/ 27 декабря 2008

У меня есть дерево объектов, которое выглядит примерно так:

           Ball
          /    \
  LegalBall    IllegalBall

И у меня есть 2 метода:

class o {
AddBall(LegalBall l)
AddBall(IllegalBall i)
}

в другом классе я бы хотел сделать следующее:

o.AddBall(myBall);

, где myBall имеет тип Ball. И заставить его вызывать правильный метод в зависимости от подтипа. Видимо, я не могу этого сделать ... аргументы не применимы.

Кто-нибудь знает, как я могу добиться того, чего хочу? Или если есть хорошая работа вокруг

Спасибо

РЕДАКТИРОВАТЬ: приложение, которое я пытаюсь создать, это тип Cricket Scorecard. Поэтому, в зависимости от типа шара, который должен быть использован, различные другие элементы должны меняться.

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

Надеюсь, это не слишком плохое объяснение моего первоначального намерения.

Ответы [ 8 ]

6 голосов
/ 27 декабря 2008

Вы можете использовать шаблон Visitor .

class Basket {
    void AddBall(LegalBall l) {
        System.out.println("LegalBall added to basket");
    }

    void AddBall(IllegalBall i) {
        System.out.println("IllegalBall added to basket");
    }
}

interface Ball {
    void AddBall(Basket b);
}

class LegalBall implements Ball {
    void AddBall(Basket b) {
        b.AddBall(this);
    }
}

class IllegalBall implements Ball {
    void AddBall(Basket b) {
        b.AddBall(this);
    }
}

или, если говорить более обобщенно:

interface BallVisitor {
    void visit(LegalBall l);
    void visit(IllegalBall i);
}

interface Ball {
    void accept(BallVisitor v);
}

class LegalBall implements Ball {
    void accept(BallVisitor v) {
        v.visit(this);
    }
}

class IllegalBall implements Ball {
    void accept(BallVisitor v) {
        v.visit(this);
    }
}

class Basket implements BallVisitor {
    void visit(LegalBall l) {
        System.out.println("LegalBall added to basket");
    }

    void visit(IllegalBall i) {
        System.out.println("IllegalBall added to basket");
    }
}
3 голосов
/ 27 декабря 2008

Вы должны попытаться реализовать только один метод:

class o {
AddBall(Ball b)
}

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

0 голосов
/ 27 декабря 2008

Используйте шаблон посетителя. Делать это без обременительно и было предметом этого вопроса: Обход статического метода Java, диспетчеризация без шаблонов двойной отправки / посетителя

Однако, не могли бы вы заявить о своей первоначальной проблеме? Я имею в виду, зачем вам эти две перегрузки для метода Add? Может быть, вы можете решить эту проблему совершенно по-другому, не полагаясь на динамическую рассылку, например, шаблон посетителя?

0 голосов
/ 27 декабря 2008

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

Я бы оставил тип Ball и сделал бы легальным / нелегальным свойство этого типа. У вас будет только o.add (Ball), в котором вы проверяете на легальность / нелегальность на основе некоторых свойств или с помощью метода, например isLegal ().

Если приведенный выше подход не кажется разумным, то нецелесообразно иметь один метод для добавления двух (очень) различных типов, и предложенное вами отношение подтипирования не будет подходящим вариантом.

0 голосов
/ 27 декабря 2008

Я согласен с использованием Посетителя.

Кроме того, если у вас нет доступа к иерархии Ball (доступ к исходному коду) или просто не хотите что-то там изменять; Вы можете изменить свой класс клиента и принять решение оттуда.

Плохо, конечно, то, что вы получите много операторов if / elseif.

Вам нужно будет добавить универсальный метод ( add (Ball) ) и оттуда вызвать другие. Это быстро, легко и грязно.

:)

public class Test {
    public static void main( String [] args ) { 
        Ball ball = new IllegalBall();
        Test test = new Test();
        test.add( ball );
        test.add( new IllegalBall() );
        test.add( new LegalBall() );
    }
    private void add( Ball ball ){
        System.out.println("Generic method: I'll have someone handling this : "  + ball );
        if( ball instanceof IllegalBall ) {
            add( ( IllegalBall ) ball );
        } else if( ball instanceof LegalBall ) {
            add( ( LegalBall ) ball );
        }
    }
    private void add( IllegalBall ball ){
        System.out.println("illega-ball: I won't do anything about it! " + ball );
    }
    private void add( LegalBall ball ) { 
        System.out.println("legal-ball: Hey this is legal I'll do my best!! " + ball );
    }
}

class Ball {}
class IllegalBall extends Ball {}
class LegalBall extends Ball {}

Кстати, если у вас нет ссылки напрямую, компилятор отправит ее правильному методу, как в последних 2 вызовах.

Как видите, вам просто нужно добавить следующий код:

private void add( Ball ball ){
    System.out.println("Generic method: I'll have someone handling this : "  + ball );
    if( ball instanceof IllegalBall ) {
        add( ( IllegalBall ) ball );
    } else if( ball instanceof LegalBall ) {
        add( ( LegalBall ) ball );
    }
}
0 голосов
/ 27 декабря 2008

Хорошо, есть шаблон посетителей, как упомянуто выше. Если вы не можете или не хотите изменять Ball, LegalBall или IllegalBall, то вы можете попробовать создать одну ветвь метода, основанную на типе шара. Обратите внимание, что если вы позже добавите QuasiLegalBall, этот код сломается. Общий случай, который вы упомянули, сложен, потому что существование LegalBall и IllegalBall не мешает существованию Balls, которые не вписываются в два описанных вами типа (по крайней мере, с точки зрения языка).

class o {
    public void AddBall(Ball b) 
    { 
        if (b instanceof LegalBall) {AddLegalBall(b); }
        else if (b instanceof IllegalBall) {AddIllegalBall(b); }
        else { /*error, new type of ball created*/ }
    }
    private void AddLegalBall(LegalBall b) { }
    private void AddIllegalBall(IllegalBall b) { }
    }
 }
0 голосов
/ 27 декабря 2008

Вы можете (или не можете) хотеть (часть) шаблона посетителя.

Добавить метод к Ball:

public abstract void addTo(o o);

Реализация в LegalBall и IllegalBall как

public void addTo(o o) {
    o.add(this);
}
0 голосов
/ 27 декабря 2008

Как вы ожидаете, что он узнает, кому позвонить? Что отличает два метода, которые вы ожидаете, что они будут вызывать один, а не другой?

...