Передача суперкласса в качестве параметра методу, ожидающему подкласс

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

           Ball
          /    \
  LegalBall    IllegalBall

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

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

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

o.AddBall(myBall);

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

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

Спасибо

Обновлено: приложение, которое я пытаюсь создать, является типом карты показателей Cricket. Таким образом, в зависимости от типа шара должны меняться другие элементы.

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

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

Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
5
0
13 884
7
Перейти к ответу Данный вопрос помечен как решенный

Ответы 7

Попробуйте реализовать только метод один:

class o {
AddBall(Ball b)
}

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

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

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

public abstract void addTo(o o);

Реализован в LegalBall и IllegalBall как

public void addTo(o o) {
    o.add(this);
}
Ответ принят как подходящий

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

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");
    }
}

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

Rocco 27.12.2008 00:52

Было бы очень странно добавлять методы «посещения» и «принятия» к обычному интерфейсу.

Tom Hawtin - tackline 27.12.2008 01:54

Ну вот и шаблон посетителя, как упоминалось выше. Если вы не можете или не хотите изменять 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) { }
    }
 }

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

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

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

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

:)

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 {}

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

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

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 );
    }
}

Ужасный. Это хрестоматийный пример того, как "instanceof" и перегрузка метода являются запахом кода для плохого дизайна. Вы злоупотребляете системой типов.

eljenso 29.12.2008 17:12

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

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

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

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

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

Другие вопросы по теме