Java: возвращение класса, который реализует интерфейс, который имеет вывод типа - PullRequest
8 голосов
/ 06 июня 2019

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

private class ParentObject { }

private class ChildObject extends ParentObject { }

private interface Template<T extends ParentObject> {
    void doSomething(T object);
}

private class TemplateImpl implements Template<ChildObject> {
    public void doSomething(ChildObject object) { }
}

Итак, у меня есть некоторые классы, которые наследуются от Template, которые делают что-то с объектом, который наследуется от ParentObject (в этом посте я разместил только один из них). Теперь эта проблема у меня возникает, когда я пытаюсь сгенерировать один из этих классов шаблонов, я получаю ошибку «несовместимые типы», когда я пытаюсь сделать это:

private class Factory {
    public <T extends ParentObject> Template<T> generate() {
        return new TemplateImpl(); // here is the error
    }
}
private class Service {
    public <T extends ParentObject> void magic() {
        Factory f = new Factory();
        Template<T> a = f.generate();
    }
}

Или я получаю предупреждение о "непроверенном назначении" (код работает так, как задумано, но если я делаю что-то не так, я бы лучше это исправил!), Когда я делаю это:

private class AlternativeFactory {
    public Template generate() {
        return new TemplateImpl();
    }
}
private class Service {
    public <T extends ParentObject> void magic() {
        AlternativeFactory af = new AlternativeFactory();
        Template<T> b = af.generate(); // warning here
    }
}

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

Спасибо!

РЕДАКТИРОВАТЬ: На самом деле, фабрика, которую я хочу реализовать, выглядит примерно так, и кажется, что здесь возникает проблема с выводом типа:

private class Factory {
    public Template<T extends ParentObject> generate(int option) {
        switch (option) {
            case 1:
                return new TemplateA(); // implements Template<A> where A extends ParentObject
            case 2:
                return new TemplateB(); // implements Template<B> where B extends ParentObject
            default:
                throw new IllegalArgumentException();
        }
    }
}

РЕДАКТИРОВАТЬ: решил пойти с кодом, который я предоставил выше (AlternativeFactory) и просто использовать SuppressWarnings в методе обслуживания, который вызывает фабрику, так как кажется, что я надеялся достичь не возможно. Я знаю, что для этого используются необработанные типы и это плохая практика, но у меня есть много тестов вокруг этих объектов Template и AlternativeFactory, включая проверки типов, так что сейчас это нужно сделать.

Ответы [ 2 ]

6 голосов
/ 06 июня 2019

TemplateImpl совместим только с Template<ChildObject>, поэтому new TemplateImpl() не может быть допустимым возвращаемым значением для public <T extends ParentObject> Template<T> generate() - поскольку T может быть другим подклассом ParentObject

Самое простое изменение - сделать Template классом:

private class Template<T extends ParentObject> {
    void doSomething(T object){/*implement*/}
}

или, если это должен быть интерфейс, сделайте также TemplateImpl generic:

private class TemplateImpl<T extends ParentObject> implements Template<T> {
    public void doSomething(ChildObject object) { }
}

Что позволит вашей фабрике использовать параметр типа:

private class Factory {
    public <T extends ParentObject> Template<T> generate() {
        return new TemplateImpl<T>(); //T is not bound to ChildObject
    }
}

Если все, что вам нужно, это убедиться, что TemplateImpl работает только как Template<ChildObject>, тогда ваш фабричный метод не должен быть универсальным:

private class TemplateImpl implements Template<ChildObject> {
    public void doSomething(ChildObject object) { }
}

//and the factory:
private class Factory {
    public Template<ChildObject> generate() {
        return new TemplateImpl(); //TemplateImpl is a Template<ChildObject>
    }
}

Следует избегать вашего второго решения, поскольку оно использует необработанные типы.

2 голосов
/ 06 июня 2019

Вы ошиблись тем, что TemplateImpl не является универсальным. У него уже есть определение. И вы генерируете () метод параметризован. TemplateImpl вынужден использовать ChildObject, в то время как ваш generate () говорит, что должен возвращать «нечто», расширяющее Parent. В качестве обходного пути можно привести оператор возврата к шаблону.

...