Java передает универсальный класс с параметрическим перечислением в качестве параметра другому универсальному объекту

#java #generics #enums #parameterization

#java #общие #перечисления #параметризация

Вопрос:

Я пытаюсь реализовать параметризованный класс в Java с перечислением в качестве параметра типа. Все работает нормально, за исключением того, что, если вы посмотрите на приведенный ниже код, есть анонимный класс Car.Creator с параметром K . Но, конечно, вместо K , должно быть CarObject<T> , но это не так просто. Если я вставлю CarObject<T> на K место, то я получу синтаксическую ошибку. Может кто-нибудь объяснить, возможно ли что-то подобное, и, возможно, предоставить какой-нибудь пример кода.

 public class CarObject<T extends Enum<T>>
{
  public Map<T, String> values;
  private Class<T> typeClass;

  public CarObject(Class<T> clazz)
  {
    typeClass = clazz;
    values = new EnumMap<T,String>(typeClass);
  }


  public static final Car.Creator<K> Creator = new Car.Creator<K>()
  {
    public K create()
    {
      return new K();
    }


    public K[] newArray(int size)
    {
      return new K[size];
    }
  }
}
  

Я могу привести вам пример из официальной документации Android (посмотрите код в разделе «Обзор класса»), где это работает отлично. Я думаю, что в Android происходит какая-то магия. Я пытаюсь сделать то же самое — реализовать разделяемый интерфейс. Я только что составил этот пример без implements Parcelable и других вещей, потому что я подумал, что, возможно, это просто синтаксический сахар :).

Ответ №1:

Посмотрите на следующее утверждение —

 public static final Car.Creator<K> Creator = new Car.Creator<K>() {
    public K create() {
        return new K();
    }
    public K[] newArray(int size) {
        return new K[size];
    }
}; // <------ missing a semicolon here
  

Похоже, у вас есть универсальный создатель класса, определенный как статический внутренний класс в классе Car. И здесь вы пытаетесь использовать его и создать его экземпляр, поэтому не можете использовать K здесь, если это не класс или интерфейс, определенный где-то.

Я думаю, что следующий пример объясняет, что я имею в виду —

 // you are not defining a class here so no parameter instead use a real class
public static final List<K> list = new List<K>() { /*....*/ };
  

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

1. Как я уже упоминал, K это просто заполнитель для этого примера. Я хочу вставить CarObject<T> значение K .

2. Да, я понимаю, что вы имеете в виду, но K есть ли просто для иллюстрации местоположения в коде, где я хочу разместить CarObject<T> .

3. @janisg: поскольку вы использовали static модификатор, Creator объект является статическим членом CarObject класса, а не какого-либо конкретного экземпляра CarObject . Таким образом, к нему можно получить доступ как CarObject.Creator . Таким образом, он не имеет никакого доступа к переменной T типа, которая имеет значение только для конкретных конкретных экземпляров CarObject like CarObject<SomeSpecificEnum> . У вас может быть либо создатель, который может создавать CarObject<SomeSpecificEnum> s, либо создатель, который является статическим, но не оба.

Ответ №2:

Сделайте метод нестатическим в соответствии с сообщением компилятора, но в любом случае у вас есть несколько других серьезных проблем с дизайном. create() нуждается в clazz откуда-то, и вы не можете возвращать универсальные массивы.

РЕДАКТИРОВАТЬ Я обычно не публикую решения с кодом, но вы, кажется, довольно смущены.

 public class CarObject<T extends Enum<T>>
{

    public Map<T, String> values;
    private Class<T> typeClass;

    public CarObject(Class<T> clazz)
    {
        typeClass = clazz;
        values = new EnumMap<T, String>(typeClass);
    }
    public final Car.Creator<CarObject<T>> Creator = new Car.Creator<CarObject<T>>()
    {

        @Override
        public CarObject<T> create()
        {
            return new CarObject<T>(typeClass);
        }

        @Override
        public CarObject<T>[] newArray(int size)
        {
            return new CarObject[size];
        }
    };
}
  

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

1. Да, я могу вернуться new K() , потому что у меня есть рабочий код для этого с базовым непараметрическим классом — K например, когда вместо есть CarObject без параметра.

2. @janisg Нет, ты не можешь. Так говорит компилятор. Вы можете вернуться new CarObject<T>(typeClass); . Это не одно и то же.

3. Я могу привести вам пример из официальной документации Android (посмотрите код в разделе «Обзор класса»), где это работает отлично. Я думаю, что в Android происходит какая-то магия. Я пытаюсь сделать здесь то же самое — реализовать разделяемый интерфейс. Я только что составил этот пример, потому что я подумал, что, возможно, это просто синтаксический сахар :).

4. @janisg: Если K это конкретный тип , то да, вы можете (это то, что есть в примере Android). Если K это параметр универсального типа , то нет, вы не можете.

5. Если K CarObject да, то все в порядке, а если K CarObject<T> да, то это не сработает? Верно?

Ответ №3:

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

Аргументы универсального типа (как T в вашем примере) принадлежат экземплярам класса, а не самому классу.

Статические члены (как Creator в вашем примере) принадлежат самому классу, а не экземплярам класса.

Тот факт, что Creator это анонимный внутренний класс, реализующий универсальный интерфейс, здесь немного отвлекающий маневр. Проблема просто в том, что, независимо от того, что Creator есть, пока оно есть static , оно не может получить доступ к типу T .

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

1. Я думаю, что в Android этот конкретный объект должен быть static . Из документов Android: «Классы, реализующие интерфейс Parcelable, также должны иметь статическое поле с именем CREATOR, которое является объектом, реализующим Parcelable. Интерфейс создателя. »

2. Итак, последний вопрос — если существует какой-либо другой способ, как передать этот класс с универсальным типом в Creator ?

3. @janisg: Я думаю, это означает, что классы, которые реализуют Parcelable , не могут быть универсальными. К сожалению, я недостаточно знаю об Parcelable интерфейсе, чтобы помочь вам в дальнейшем. Возможно, вам следует создать новый, более конкретный вопрос, поскольку вы сейчас не знакомы с Java и generics.