Вызов универсального интерфейса Java с абстрактным параметром

Я знаю, что есть много подобных вопросов, но мне не удалось найти красивое и чистое решение, если это вообще возможно. Я реализую общий интерфейс с подклассами абстрактного типа. Проблема в том, что когда я их вызываю, я должен либо выполнить приведение типа в переключателе / ​​случае, либо привести тип в каждом методе внутри реализаций интерфейса, и я не могу придумать хороший и чистый подход ... Я лучше просто напишу на короткий пример.

// An abstract type with 2 implementations...
public abstract class ObjTypeAbstract {}

public class ObjType extends ObjTypeAbstract {}
public class ScriptType extends ObjTypeAbstract {}

Теперь процессор для обоих типов с интерфейсом

interface ProcessorInterface<T extends ObjTypeAbstract> {
    public void abcMethod(T obj);
}

public class ObjProcessor implements ProcessorInterface<ObjType> {
    public void abcMethod(ObjType obj) {}
}
public class ScriptProcessor implements ProcessorInterface<ScriptType> {
    public void abcMethod(ScriptType obj) {}
}

То, с чем я борюсь, - это способ вызова этих процессоров на основе ObjAbstractType. У меня есть единственный класс, который серверы как промежуточное ПО ?? или как бы это назвать:

Идея заключалась в том, чтобы просто получить нужный процессор с помощью одного переключателя / корпуса:

public class Processor {
    private ProcessorInterface objProcessor = new ObjProcessor();
    private ProcessorInterface scriptProcessor = new ScriptProcessor();

    public methodAbc(ObjAbstractType obj) {
        getProcessor(obj).abcMethod(obj);
    }

    private ProcessorInterface getProcessor(ObjAbstractType obj) {
        if (obj instanceof ObjType) {
            return objectProcessor;
        } else if (obj instanceof ScriptType) {
            return scriptProcessor;
        }

        return nullProcessor;
    }
}

Это то, что я хотел бы иметь, он также заботится о приведении типа objAbstract к фактическому типу для abcMethod, проблема в том, что это приводит к предупреждению RawType, которое не нарушит код, но я бы хотел избавиться от Это.

И вот где я застрял ... потому что, если я приведу процессоры к определенному типу, например:

private ProcessorInterface<ObjType> objProcessor = new ObjProcessor();
private ProcessorInterface<ScriptType> scriptProcessor = new ScriptProcessor();

Я не смогу вернуть абстрактный из метода getProcessor, поэтому мне пришлось бы реализовать эти интерфейсы с помощью ObjAbstractType со всем его методом и иметь приведение типов во все методы каждого процессора, например:

public class ScriptProcessor implements ProcessorInterface<ObjAbstractType> {
    public void abcMethod(ObjAbstractType obj) {
        ScriptType scr = (ScriptType) obj;
    }
}

Другое решение может заключаться в переключателе / ​​корпусе внутри класса промежуточного программного обеспечения процессора и приведении в нем ObjAbstractType, но мне пришлось бы написать этот переключатель внутри abcMethod и всех остальных или из метода getProcessor, который возвращает как процессор, так и приведенный ObjType ... поэтому я Придется вернуть какой-нибудь dto, содержащий и то, и другое. : /

Есть ли у вас какие-либо идеи / шаблоны, которые могут помочь мне избавиться от предупреждения о вызове RawType без расширения кода дополнительными переключателями / регистрами или приведениями типов? Желаю тебе хорошего дня, и я буду рад любому обсуждению, Дэвид.

В этом коде действительно много ошибок. Вы пропустили некоторые важные вещи, такие как ключевое слово extends для подклассов. Что такое GmAbstractType, который вы используете, но не декларируете? Пожалуйста, исправьте его, если вы хотите, чтобы ваш вопрос был удобочитаемым и полезным для других.

davidxxx 09.09.2018 12:01

Правильно, я обновил пример кода ... это были просто ошибки типа, поскольку я написал упрощенную версию вместо фактического кода ... Gm <=> Obj ... и о расширениях забыли. :)

David Horáček 09.09.2018 13:13

Я предполагал все это, но всегда лучше прояснить это. Очень хорошее обновление.

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

Ответы 2

Ответ принят как подходящий

Вам нужен способ сохранить сопоставление между классом ObjTypeAbstract и экземпляром ProcessorInterface. Вы можете использовать Map, который связывает ObjTypeAbstract (как ключ) с ProcessorInterface (как значение). Что касается проблемы необработанного типа, вы можете использовать ProcessorInterface<? extends ObjTypeAbstract> для объявленной переменной, но вам все равно потребуется выполнить небезопасное приведение к ProcessorInterface<ObjTypeAbstract>, чтобы иметь возможность вызывать ProcessorInterface.abcMethod() с параметром объявленного типа ObjTypeAbstract. Этот слепок неизбежен с вашим настоящим дизайном.

Это могло дать что-то вроде:

public class Processor {

    private Map<Class<? extends ObjTypeAbstract>, ProcessorInterface<? extends ObjTypeAbstract >> map = new HashMap<>();

    public Processor(){
        map.put(ObjType.class, new ObjProcessor());
        map.put(ScriptType.class, new ScriptProcessor());   
    }
    public void methodAbc(ObjTypeAbstract obj) {
        @SuppressWarnings("unchecked")
        ProcessorInterface<ObjTypeAbstract> processorInterface = (ProcessorInterface<ObjTypeAbstract>) map.get(obj.getClass());
        processorInterface.abcMethod(obj);
    }

}

Хороший способ. Особенно мне нравится тот факт, что расширение Processor новой реализацией теперь означает одну строку внутри конструктора. Спасибо.

David Horáček 09.09.2018 14:30

Хорошо, я наконец-то должен попробовать, и, к сожалению, он не вернет процессор с карты. Я реализовал его точно так же »(ProcessorInterface <ObjAbstractType>) map.get (obj); ' возвращает null. .. буду смотреть на это.

David Horáček 09.09.2018 14:36

Добро пожаловать :) Насчет вашего второго комментария, это удивительно. Я только что протестировал компиляцию, и она работает.

davidxxx 09.09.2018 15:11

Нашел, потребовалось вызвать map.get (obj.getClass ()); ... Мне не хватало этой части .getClass ().

David Horáček 09.09.2018 15:26

@ Дэвид Хорачек Ой, извините, я этого не заметил. Я только что тестировал компиляцию. Вы быстро поймали проблему, хорошо! Я обновился, чтобы быть правильным.

davidxxx 09.09.2018 15:29

Я не думаю, что есть существенно более элегантный способ обойти некоторую форму логики instanceof. Однако, если вы добавляете какие-то типы в getProcessor, необходимости в приведении не должно быть.

    public <T extends ObjTypeAbstract> ProcessorInterface<T> getProcessor(Class<T> theClass) {
        if (theClass.isAssignableFrom(ObjType.class)) {
            return objProcessor;
        } else if (theClass.isAssignableFrom(ScriptType.class)) {
            return scriptProcessor;
        }
        return null;
    }

Затем это можно назвать так:

    ProcessorInterface<ScriptType> scriptProcessor = new Processor().getProcessor(ScriptType.class);
    ProcessorInterface<ObjType> objProcessor = new Processor().getProcessor(ObjType.class);

Это выглядит интересно (никогда не думал о таком объявлении), но не может разобраться. Поскольку я возвращаю objProcessor, который является типом ProcessorInterface <ObjType>, он по-прежнему выдает ошибку, что ProcessorInterface <T> несовместим с ним. В любом случае я собираюсь заглянуть в это глубже. :) .. ошибка отображается в строке возврата внутри метода getProcessor.

David Horáček 09.09.2018 14:01

Работает на моей машине;) Кроме того, решение, которое вы отметили как правильное, использует непроверенное приведение и не защищает от неправильной настройки данных: map.put (ObjType.class, new ScriptProcessor ());

Fritz Duchardt 09.09.2018 16:21

@Fritz Duchardt: Считаете ли вы, что серия условных операторов - лучшее решение? По поводу «не защищает от неправильной настройки данных», как вы думаете, условные выражения больше защищают от опечатки? Вы можете написать if (theClass.isAssignableFrom(ObjType.class)) { return scriptProcessor; }, и компилятор все равно компилируется нормально. Наконец, я не понимаю, как ваш метод может компилироваться без приведения в порядок. T - это параметризованный тип, привязанный к методу. Любой объявленный тип ProcessorInterface, который вы возвращаете, должен соответствовать T.

davidxxx 09.09.2018 18:46

Где нижний регистр в ProcessorInterface <ObjType> objProcessor = new Processor (). GetProcessor (ObjType.class)?

Fritz Duchardt 09.09.2018 19:07

Я сослался на операторы возврата внутри getProcessor().

davidxxx 09.09.2018 19:27

Тип возврата getProcessor может быть <T extends ObjTypeAbstract>, что позволяет возвращать ScriptProcessor или ObjProcessor в нижнем регистре.

Fritz Duchardt 09.09.2018 19:34

Это то, что я сказал, но в вашем коде нет приведения.

davidxxx 09.09.2018 19:43

Извините, конечно, я хочу сказать "без пропуска" :)) Естественно, при использовании ограниченных универсальных типов нет необходимости в приведении типов.

Fritz Duchardt 09.09.2018 19:55

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