#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
likeCarObject<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.