Ошибка с использованием общего возвращаемого типа (несовместимые типы: не могут быть преобразованы в T)

#java #generics #signature

#java #общие #подпись

Вопрос:

 public class Main{
    private MyQueue queue = null;
    private MyStack stack = null;
    private MyList list = null;

    public static void main(String args[]){
        Main m = new Main();
        m.run();
    }
    
    public void run(){
        // only one data structure is not null.
        // for example, let's suppose that it's queue
        this.queue = new MyQueue();
        
        int intToAdd = 6;   // I want to add this number
        this.selectStruct().insert(intToAdd);       // I want to invoke the "insert" method of the current data structure in use
    }
    
    private <T> T selectStruct(){       // this should check with data structure is in use (so which of them is not null) and then return the reference
        if (this.queue!=null){
            return this.queue;
        }
        if (this.stack!=null){
            return this.stack;
        }
        if (this.list!=null){
            return this.list;
        }
    }
}
 

Я хочу использовать этот метод, вызываемый selectStruct для выбора текущей используемой структуры данных и возврата ссылки. И из этой ссылки затем вызывается insert вызов, который вызывает правильный метод insert правильного класса.

Проблема в том, что у меня есть несколько ошибок. Я никогда не использовал дженерики, поэтому я немного смущен.

 $ javac Main.java
Main.java:22: error: incompatible types: MyQueue cannot be converted to T
                        return this.queue;
                                   ^
  where T is a type-variable:
    T extends Object declared in method <T>selectStruct()
Main.java:25: error: incompatible types: MyStack cannot be converted to T
                        return this.stack;
                                   ^
  where T is a type-variable:
    T extends Object declared in method <T>selectStruct()
Main.java:28: error: incompatible types: MyList cannot be converted to T
                        return this.list;
                                   ^
  where T is a type-variable:
    T extends Object declared in method <T>selectStruct()
Main.java:17: error: cannot find symbol
                this.selectStruct().insert(intToAdd);
                                   ^
  symbol:   method insert(int)
  location: class Object
4 errors
 

Ответ №1:

Позвольте MyQueue , MyStack , и MyList реализовать тот же интерфейс, содержащий методы, которые вы хотите, чтобы все ваши структуры данных реализовали, например:

 public interface IDataStructure {
    void insert(int intToAdd);
}
 

Тогда ваши классы должны выглядеть следующим образом:

 public class MyQueue implements IDataStructure{

    @Override
    public void insert(int intToAdd) {
        // TODO 
    }

}

public class MyStack implements IDataStructure{

    @Override
    public void insert(int intToAdd) {
        // TODO 
    }

}

public class MyList implements IDataStructure{

    @Override
    public void insert(int intToAdd) {
        // TODO 
    }

}
 

Наконец, вы можете изменить свой selectStruct() метод, чтобы вернуть IDataStructure:

 private IDataStructure selectStruct() {
    if (this.queue != null) {
        return this.queue;
    }
    if (this.stack != null) {
        return this.stack;
    }
    if (this.list != null) {
        return this.list;
    }
    return null;
}
 

Комментарии:

1. спасибо за ответ 🙂 поскольку myQueue, MyStack и myList уже реализуют некоторые определенные интерфейсы, я подумал, что вместо создания интерфейса IDataStructure создать вместо него абстрактный класс IDataStructure, который реализует ОДНИ и те ЖЕ интерфейсы (они одинаковы для каждой структуры данных). Итак, после этого вместо написания инструментов внутри myQueue, MyStack и myList я просто пишу «расширяет IDataStructure», и работа выполнена. Я сделал это, и это работает, но я хотел знать, хорошо ли это для объектно-ориентированного программирования.

2. @Sperly Просто чтобы понять это правильно: все ваши структуры данных должны реализовывать несколько интерфейсов, чтобы функционировать как структуры данных? Тогда ваш выбор абстрактного класса, который реализует эти интерфейсы, является очень хорошим. Чтобы привести пример, почему: представьте, что вы решили, что структуре данных нужна другая конкретная логика, или вы решили удалить интерфейс. В этих случаях вам необходимо изменить все конкретные классы. В вашем случае большая часть изменений будет внесена в ваш абстрактный класс. Так что да, я думаю, что ваша идея хороша.

3. Но, пожалуйста, придумайте для этого другое название. I In IDataStructure означает интерфейс. Вам нужно что-то вроде AbstractDataStructure . Но, конечно, это именование зависит от соглашения, которому вы должны следовать.

Ответ №2:

Когда вы объявляете метод с параметром типа T (like private <T> T selectStruct() ), вы говорите, что пользователь этого метода может выбрать любой тип, который он хочет использовать для этого метода.

Итак, в теле selectStruct вы не знаете, какой тип будет использовать вызывающий, и, конечно, вы не можете вернуть какой-либо конкретный тип. Вот почему каждое ваше предложение return, подобное return this.queue; , приведет к ошибке.

Комментарии:

1. спасибо за ответ. значит, нет никакого способа исправить эту проблему?

2. Вы должны изменить способ использования общего. Ответ @Michael Chatiskatzi показал вам, как это сделать. Я просто говорю вам, почему то, как вы его используете, не поддерживается.

3. да, я знаю, мне просто было любопытно, как я мог бы сделать это с дженериками.