Я пытаюсь записать класс, который имеет универсальную членскую переменную, но не, саму, универсален. А именно, я хочу сказать, что у меня есть Список значений "некоторого типа, который реализует сопоставимый с собой", так, чтобы я мог назвать вид в том списке... Я надеюсь, что это имеет смысл.
Конечный результат того, что я пытаюсь сделать, состоит в том, чтобы создать класс, таким образом, что я могу создать экземпляр упомянутого класса с массивом (любой данный тип) и иметь его, генерируют строковое представление для того списка. В реальном коде я также передаю в классе типов, в которых я являюсь передающим:
String s = new MyClass(Integer.class, 1,2,3).asString();
assertEquals("1 or 2 or 3", s);
String s = new MyClass(String.class, "c", "b", "a").asString();
assertEquals("\"a\" or \"b\" or \"c\"", s);
Первоначально я даже не хотел передавать в классе, я просто хотел передать в значениях и иметь код, исследуют полученный массив для выбирания класса значений..., но это давало мне проблемы также.
Следующее является кодом, который я имею, но я не могу придумать правильное заклинание для помещения для типа переменной.
public class MyClass {
// This doesn't work as T isn't defined
final List<T extends Comparable<? super T>> values;
public <T extends Comparable<? super T>> MyClass (T... values) {
this.values = new ArrayList<T>();
for(T item : values) {
this.values.add(item);
}
}
public <T extends Comparable<? super T>> List<T> getSortedLst() {
Collections.sort(this.values);
return this.values;
}
}
ошибка на строке объявления переменной:
Syntax error on token "extends", , expected
Любая справка очень ценилась бы.
Править: обновленный код для использования Списка вместо массива, потому что я не уверен, что он может быть сделан с массивами.
@Mark: От всего я читал, я действительно хочу сказать "T, тип, который сопоставим с собой", не только "T тип, который сопоставим". Однако следующий код не работает также:
public class MyClass {
// This doesn't work
final List<? extends Comparable> values;
public <T extends Comparable> MyClass (T... values) {
this.values = new ArrayList<T>();
for(T item : values) {
this.values.add(item);
}
}
public <T extends Comparable> List<T> getSortedLst() {
Collections.sort(this.values);
return this.values;
}
}
ошибка на добавляет строку:
The method add(capture#2-of ? extends Comparable) in the type List<capture#2-of ? extends Comparable> is not applicable for the arguments (T)
ошибка на строке вида:
Type mismatch: cannot convert from List<capture#4-of ? extends Comparable> to List<T>
Заключение:
То, к чему это сводится, это появляется, то, что Java не может вполне обработать то, что я хочу сделать. Проблема состоит в том, потому что то, что я пытаюсь сказать:
Я хочу список объектов, которые сопоставимы против себя, и я создаю целый список сразу из данных, переданных в при создании.
Однако Java видит, что я имею тот список и не могу закрепить ту всю информацию для моей ситуации, доступно во время компиляции, так как я мог попытаться добавить вещи к списку позже и, должный ввести стирание, это не может гарантировать ту безопасность. Не действительно возможно передать к Java условия, вовлеченные в мою ситуацию, не применяя универсальный тип к классу.
Я думаю, что простой ответ заключается в том, что вы не можете этого сделать. Если тип одного из атрибутов класса зависит от параметра типа, этот параметр должен быть объявлен на уровне класса. И я не думаю, что это «имеет смысл» иначе.
Если T
в вашем примере не является параметром типа класса, что это такое? Это не может быть параметром типа метода, потому что этот тип определяется способом вызова метода. (Если метод вызывается в разных статических контекстах с разными предполагаемыми типами для T
, каков условный тип T
в контексте объявления атрибута?)
Итак, чтобы верните это к тому, что вы пытаетесь сделать здесь, экземпляр MyClass
будет содержать элементы определенного типа, и вы хотите иметь возможность вставлять и удалять элементы статически безопасным способом. Но в то же время вы не хотите, чтобы вы могли сказать, что это за тип. Итак, как компилятор должен статически различать экземпляр MyClass
, который содержит (скажем) Integer
объектов, и экземпляр, содержащий объекты String
?
Я даже не думаю, что вы могли бы реализовать это с помощью явной динамической проверки типов. (Я думаю, что стирание типа означает, что реализация метода getSortedList ()
не может определить, какой фактический тип привязан к его возвращаемому типу.)
Нет. Реальное решение - сделать MyClass
универсальным классом, который объявляет параметр типа T
; например
public class MyClass <T extends Comparable<T>> {
и удалите объявление параметра типа уровня метода T
из двух методов.
Здесь много непроверенных предупреждений, но в принципе нет необходимости хранить List
как нечто иное, чем нечто, содержащее вещи, которые, как вы знаете, являются сравнимыми
. Вы применяете необходимые правила в конструкторе, и все остальное должно быть в порядке. Как насчет чего-то вроде этого:
public class MyClass {
final private List<Comparable> values;
public <T extends Comparable<? super T>>MyClass(T... values){
this.values = new ArrayList<Comparable>();
for(T item : values) {
this.values.add(item);
}
}
public <T extends Comparable<? super T>> List<T> getSortedLst() {
Collections.sort(this.values);
return (List<T>)this.values;
}
}
Быстрый тест с использованием следующего показывает, что для классов, реализующих Comparable (таких как Integer и String) MyClass
ведет себя как ожидалось, но выдаст ошибку компиляции для классов, не реализующих Comparable
:
class Junk { }
public static void main(String[] args){
MyClass s = new MyClass(1,2,3);
System.out.println(s.getSortedLst());
MyClass a = new MyClass("c", "a", "b");
System.out.println(a.getSortedLst());
MyClass c = new MyClass(new Junk());
}
Рассмотрите это так (то, что я собираюсь сказать, не соответствует действительности. Но это иллюстрирует, почему вам нужно делать то, что вам нужно):
class Foo<T>
{
private T value;
T getValue() { return value; }
void setValue(T val) {value = val; }
}
// some code that uses the above class
Foo<Integer> iFoo = new Foo<Integer>();
Foo<String> sFoo = new Foo<String>();
iFoo.setValue(5);
sFoo.setValue("Hello");
Когда это происходит, компилятор ( НЕ ДЕЙСТВИТЕЛЬНО ДЕЛАЕТ ТО, ЧТО Я НАСКОЛЬКО СКАЗАТЬ!) Генерирует следующий код:
class IntegerFoo
{
private Integer value;
Integer getValue() { return value; }
void setValue(Integer val) {value = val; }
}
class StringFoo
{
private String value;
String getValue() { return value; }
void setValue(String val) {value = val; }
}
// some code that uses the above class
IntegerFoo iFoo = new IntegerFoo();
StringFoo< sFoo = new StringFoo();
iFoo.setValue(5);
sFoo.setValue("Hello");
Если бы вы могли параметризовать переменные / методы экземпляра без параметризации класса, вышеуказанная вещь (ЭТО НЕ РЕАЛЬНОСТЬ!) Не сработала бы. .
То, что вы пытаетесь сделать, должно быть возможно с помощью статических методов, но я не думаю, что вы этого хотите.
Можете ли вы объяснить, почему вы хотите выполнить код, который пытаетесь сделать? Возможно, мы сможем придумать лучший способ делать то, что вы хотите, в рамках языка.
Я считаю, что следующее достигнет того, что вы хотите (более сильная типизация Comparable). Это предотвратит добавление в список объектов Comparable, не относящихся к вашему интерфейсу, и позволит использовать несколько реализаций.
public class test<T extends ComparableType> {
final List<T> values = new ArrayList<T>();
public test (T... values) {
for(T item : values) {
this.values.add(item);
}
}
public List<T> getSortedLst() {
Collections.sort(this.values);
return Collections.unmodifiableList(this.values);
}
}
public interface ComparableType extends Comparable<ComparableType> {}
public class ConcreteComparableA implements ComparableType {
@Override
public int compareTo(ComparableType o) {
return 0;
}
}
public class ConcreteComparableB implements ComparableType {
@Override
public int compareTo(ComparableType o) {
return 0;
}
}
edit:
Я знаю, что это может быть очевидно; но если вы не хотите, чтобы класс был Generic, это решение также будет работать с:
public class test {
final List<ComparableType> values = new ArrayList<ComparableType>();
public test (ComparableType... values) {
for(ComparableType item : values) {
this.values.add(item);
}
}
public List<ComparableType> getSortedLst() {
Collections.sort(this.values);
return Collections.unmodifiableList(this.values);
}
}
Требуемое ограничение типа для переменной не может быть выражено напрямую. вы можете ввести новый тип, чтобы решить эту проблему.
static class MyList<T extends Comparable<? super T>> extends ArrayList<T>{}
final MyList<?> values;
однако нет смысла быть чрезвычайно безопасным по типу в частном фрагменте кода. Generic поможет вам прояснить ваши типы, а не запутать их.
public class MyClass<T extends Comparable<? super T>> {
// This doesn't work as T isn't defined
final List<T> values;
public MyClass (T... values) {
this.values = new ArrayList<T>(Arrays.asList(values));
}
public List<T> getSortedLst() {
Collections.sort(this.values);
return this.values;
}
}
Я бы сделал так (я сделал это в виде списка или массива), если вам не нужны переменные/методы экземпляра:
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class MyClass
{
public static <T extends Comparable<T>> List<T> asSortedList(final T ... vals)
{
final List<T> temp;
temp = new ArrayList<T>(vals.length);
temp.addAll(Arrays.asList(vals));
Collections.sort(temp);
return (Collections.unmodifiableList(temp));
}
public static <T extends Comparable<T>> T[] asSortedArray(final Class<?> clazz,
final T ... vals)
{
final T[] temp;
temp = (T[])Array.newInstance(clazz,
vals.length);
System.arraycopy(vals,
0,
temp,
0,
vals.length);
Arrays.sort(temp);
return (temp);
}
public static void main(final String[] argv)
{
final List<String> list;
final String[] array;
list = MyClass2.asSortedList("c", "a", "b");
System.out.println(list);
array = MyClass2.asSortedArray(String.class, "z", "y", "x");
System.out.println(Arrays.deepToString(array));
}
}