Java - Generics - приведение универсального объекта к универсальному указанному объекту не работает

Я пытаюсь решить эту, казалось бы, простую общую проблему приведения:

Во-первых, объявление этого простого универсального объекта:

public interface GenericObject<T> {}

Во-вторых, объявление этого рабочего интерфейса:

public interface Generic { // I don't want to do Generic<T>

    <T> void setGenericObject(GenericObject<T> obj);

}

Затем давайте реализуем этот интерфейс:

public class GenericImpl implements Generic {

    private GenericObject<String> genericObject; // This is needed

    @Override
    public <String> void setGenericObject(GenericObject<String> obj) {
        genericObject = obj; // eclipse give me this error :
                             // Type mismatch: cannot convert from 
                             // interfaces.GenericObject<String> to
                             // interfaces.GenericObject<java.lang.String>
    }

}

Как я могу решить эту ошибку?

Редактировать :

На самом деле, единственный способ решить эту проблему - сделать это:

public class GenericImpl implements Generic {

    private GenericObject<String> genericObject; 

    @SuppressWarnings("unchecked") // I don't realy like this
    @Override
    public <T> void setGenericObject(GenericObject<T> obj) {
        genericObject = (GenericObject<String>) obj;
    }

}

person MonkeyJLuffy    schedule 23.11.2018    source источник
comment
Похоже на проблему XY. <String> void делает не то, что вы думаете. Вы определяете новый параметр универсального типа, используя идентификатор String. С таким же успехом можно сказать <FooBar> void. Вы должны удалить его. Единственная проблема заключается в том, что когда вы это делаете, метод неправильно переопределяет метод из интерфейса.   -  person Michael    schedule 23.11.2018
comment
Проблема в том, что Generic указывает, что любые разработчики должны иметь возможность реализовать setGenericObject для всех значений T. Ваша реализация работает только для строк, поэтому она не выполняет контракт, определенный интерфейсом. Лучший способ достичь того, чего вы хотите, — это иметь Generic<T> и GenericImpl implements Generic<String>, но вы сказали, что не хотите этого делать.   -  person Michael    schedule 23.11.2018
comment
см. заголовок stackoverflow.com/questions/7433279/   -  person chenzhongpu    schedule 23.11.2018
comment
В порядке. @ Майкл Как вы думаете, есть ли способ решить мою проблему, не делая @SuppressWarning ?   -  person MonkeyJLuffy    schedule 23.11.2018


Ответы (2)


Настоящая проблема в том, что

public <String> void setGenericObject(GenericObject<String> obj)

где String не имеет ничего общего с предполагаемым java.lang.String. Здесь String — это просто параметр типа, которому случайно присвоено имя String.

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

person chenzhongpu    schedule 23.11.2018
comment
Хорошо, я понимаю, что мой тег <String> похож на <T>, но что вы предлагаете для решения моей проблемы? И имейте в виду, что я не хочу делать Generic<T> - person MonkeyJLuffy; 23.11.2018

Дело 1:

Если T не используется в Generic, просто используйте подстановочный знак.

class Generic {
    List<?> list;
    void set(List<?> list) {
        this.list = list;
    }
    int size() {
        return list.size(); // doesn't care about T
    }
}

Случай 2:

Если T используется только как локальные переменные, объявите <T> в методе.

class Generic {
    <T> void swapFirstAndSecond(List<T> list) {
        T first = list.get(0), second = list.get(1);
        list.set(1, first);
        list.set(0, second);
    }
}

Случай 3:

Если несколько полей и методов используют один и тот же тип T, но точный тип T не важен, то делайте <T> в классе

class Generic<T> {
    List<T> list;
    void set(List<T> list) {
        this.list = list;
    }
    T getFirst() {
        return list.get(0);
    }
}

Случай 4:

Если T должен быть определенного типа, например String, то не объявляйте параметр типа <T>

class Generic {
    List<String> list;
    void set(List<String> list) {
        this.list = list;
    }
    boolean isFirstContainsSecond() {
        String first = list.get(0), second = list.get(1);
        // call String.contains here, so T must be String
        return first.contains(second);
    }
}
person yyyy    schedule 23.11.2018