Универсальная переменная экземпляра в не-универсальном классе
Я пытаюсь написать класс, который имеет универсальную переменную-член, но сам по себе не является универсальным. В частности, я хочу сказать, что у меня есть список значений "некоторого типа, который реализует сопоставимый с собой", чтобы я мог вызвать сортировку в этом списке... Надеюсь, в этом есть смысл.
конечным результатом того, что я пытаюсь сделать, является создание класса, чтобы я мог создать экземпляр указанного класса с массивом (любого заданного типа) и создать для него строковое представление список. В реальном коде я также передаю класс типов, которые я передаю:
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
любая помощь была бы очень признательна.
Edit: обновил код, чтобы использовать список вместо массива, потому что я не уверен, что это можно сделать с массивами.
@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 условия, связанные с моей ситуацией, без применения универсального типа к классу.
7 ответов
я думаю, что простой ответ заключается в том, что вы не можете этого сделать. Если тип одного из атрибутов класса зависит от параметра типа, этот параметр должен быть объявлен на уровне класса. И я не думаю, что это "имеет смысл" каким-либо другим способом.
если T
в вашем примере не является параметром типа класса, что это такое? Он не может быть параметром типа метода, поскольку этот тип определяется способом вызова метода. (Если метод вызывается по-разному статические контексты с различными выводимыми типами для T
, что такое условный тип T
в контексте объявления атрибута?)
Итак, чтобы вернуть это к тому, что вы пытаетесь сделать здесь, экземпляр MyClass
будет содержать элементы некоторого типа,и вы хотите иметь возможность вставлять и удалять элементы статически безопасным способом. Но в то же время вы не хотите быть в состоянии сказать, что это тип. Так как компилятор должен статически различают MyClass
экземпляр, который содержит (скажем) Integer
объекты и владеет String
объекты?
я даже не думаю, что вы могли бы реализовать это с явными динамическими typechecks. (Я думаю, что стирание типа означает, что реализация getSortedList()
метод не может узнать, какой фактический тип привязан к его возвращаемому типу.)
нет. Реальное решение-сделать MyClass
универсальный класс, объявляющий параметр type T
, например,
public class MyClass <T extends Comparable<T>> {
и удалите объявление параметра типа уровня метода T
из этих двух методов.
в этом есть много непроверенных предупреждений, но в принципе нет необходимости держать List
как что угодно, кроме того, что содержит вещи, которые вы знаете,Comparable
. Вы применяете правила, которые вам нужны в конструкторе, и все остальное должно быть в порядке. Как насчет чего-то вроде этого:
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;
}
}
быстрый тест с использованием следующего показывает, что для классов, реализующих сопоставимые (например, 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");
Если бы вы могли параметризовать переменные/методы экземпляра без параметризации класса, вышеуказанная вещь (которая не является реальностью! не сработает.
что вы пытаетесь сделать должны быть возможным со статическими методами, но я не думаю, что это то, что вы хотите.
вы можете объяснить, почему вы хотите сделать код, который вы пытаетесь сделать? Возможно, мы сможем найти лучший способ сделать то, что вы хотите сделать, что работает в языке.
Я считаю, что следующее достигнет того, что вы хотите (более сильный тип сопоставимого). Это предотвратит добавление сопоставимых объектов, которые не входят в ваш интерфейс, в список и позволит использовать несколько реализаций.
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:
Я знаю, что это может быть очевидно; но если вы не хотите, чтобы класс был общим, это решение также будет работать с:
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);
}
}
Я бы сделал это так (я сделал это как список или как массив), если вам действительно не нужна переменная экземпляра / методы:
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));
}
}
ограничение типа, которое вы хотите для переменной, не может быть выражено напрямую. вы можете ввести новый тип, чтобы устранить проблему.
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;
}
}