我正在尝试编写一个具有通用成员变量的类,但它本身并不是通用的。具体来说,我想说我有一个“实现与自身相似的某种类型”的值列表,因此我可以在该列表上调用sort ...我希望这是有道理的。
我正在尝试做的最终结果是创建一个类,以便我可以使用(任何给定类型)的数组创建所述类的实例,并让它为该列表生成字符串表示。在实际代码中,我还传入了我传入的类的类:
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);
最初我甚至不想传入类,我只想传递值并让代码检查生成的数组来挑选值的类......但这也给了我麻烦
以下是我的代码,但我无法想出适合变量类型的mojo。
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
非常感谢任何帮助。
编辑: 更新代码以使用List而不是数组,因为我不确定是否可以使用数组。
@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传达我的情况所涉及的条件。
答案 0 :(得分:7)
我认为简单的答案是你不能那样做。如果某个类属性的类型取决于类型参数,则必须在类级别声明该参数。而且我认为它没有任何其他方式“有意义”。
如果您的示例中的T
不是该类的类型参数,那么它是什么?它不能是方法的类型参数,因为该类型由方法的调用方式决定。 (如果在T
的不同推断类型的不同静态上下文中调用该方法,那么在属性声明的上下文中T
的名义类型是什么?)
因此,为了使这回到你在这里尝试做的事情,MyClass
的实例将保存某种类型的元素,并且您希望能够以静态类型安全的方式插入和删除元素。但与此同时,你不想说出那种类型。那么编译器应该如何静态区分保存(比如说)MyClass
个对象的Integer
实例和保存String
个对象的实例?
我甚至认为你不能用明确的动态类型检查来实现这一点。 (我认为类型擦除意味着getSortedList()
方法的实现无法找出绑定到其返回类型的实际类型。)
没有。真正的解决方案是使MyClass
成为声明类型参数T
的泛型类; e.g。
public class MyClass <T extends Comparable<T>> {
并从两种方法中删除方法级类型参数T
的声明。
答案 1 :(得分:2)
这里有很多未经检查的警告,但原则上没有必要将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;
}
}
使用以下内容进行的快速测试表明,对于实现Comparable的类(如Integer和String),MyClass
的行为与预期相同,但会为未实现Comparable
的类抛出编译错误:< / p>
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());
}
答案 2 :(得分:1)
像这样考虑(我要说的不是现实。但它说明了为什么你需要做你需要做的事情):
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");
如果你能够在没有参数化类的情况下参数化实例变量/方法,那么上面的东西(这不是真实的!)将无效。
你应该尝试使用静态方法做什么,但我认为这不是你想要的。
你能解释一下你为什么要做你想要的代码吗?也许我们可以找到一种更好的方法来做你想做的事情,并且能够在语言中运作。
答案 3 :(得分:1)
我相信以下内容将实现您的目标(更强大的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;
}
}
修改强>
我知道这可能很明显;但是如果你不希望这个类是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);
}
}
答案 4 :(得分:1)
我这样做(我把它作为一个列表或一个数组),除非你真的需要实例变量/方法:
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));
}
}
答案 5 :(得分:0)
您想要对变量的类型约束无法直接表达。您可以引入一种新类型来解决问题。
static class MyList<T extends Comparable<? super T>> extends ArrayList<T>{}
final MyList<?> values;
但是,在私有代码中没有必要非常安全。 Generic可以帮助您澄清类型,而不是混淆它们。
答案 6 :(得分:-1)
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;
}
}