#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
InIDataStructure
означает интерфейс. Вам нужно что-то вродеAbstractDataStructure
. Но, конечно, это именование зависит от соглашения, которому вы должны следовать.
Ответ №2:
Когда вы объявляете метод с параметром типа T (like private <T> T selectStruct()
), вы говорите, что пользователь этого метода может выбрать любой тип, который он хочет использовать для этого метода.
Итак, в теле selectStruct
вы не знаете, какой тип будет использовать вызывающий, и, конечно, вы не можете вернуть какой-либо конкретный тип. Вот почему каждое ваше предложение return, подобное return this.queue;
, приведет к ошибке.
Комментарии:
1. спасибо за ответ. значит, нет никакого способа исправить эту проблему?
2. Вы должны изменить способ использования общего. Ответ @Michael Chatiskatzi показал вам, как это сделать. Я просто говорю вам, почему то, как вы его используете, не поддерживается.
3. да, я знаю, мне просто было любопытно, как я мог бы сделать это с дженериками.