class Attribute<T>{
private T attr;
public Attribute(T attr){
this.attr = attr;
}
}
Class Matrix<T>{
private String name;
List<Attribute<T>> list;
public Matrix(String name, T t){
this.name = name;
list = new ArrayList<>();
list.add(new Attribute<T>(t));
}
}
interface Extractor<T> {
public List<Matrix<T>> extract();
}
InfoExtractor implements Extractor<String>{
public List<Matrix<String>> extract(){
List<Matrix<String>> matrixList = new ArrayList<>();
// The problem is here!!!!
matrixList.add(new Matrix<String>("abc"));
}
}
Document<T>{
Map<String, List<Matrix<T>>> matrixMap;
public void process(){
...
Extractor<T> extractor = (Extractor<T>) new StringExtractor(sent);
List<Matrix<T>> matrix = extractor.extract(...);
}
我的问题是,有没有办法避免将Matrix定义为泛型类型?我想避免的原因是“List<Attribute<T>>
”在多个其他类中使用,可以是私有成员变量,也可以是方法返回类型。由于属性,似乎我必须将一些其他相关类定义为泛型类型,这会导致我的问题。
在这种情况下,有没有办法不将Matrix定义为泛型,但将“list”变量保留为泛型类型?
答案 0 :(得分:2)
您的问题不在于通用实现,而在于其用法:
class InfoExtractor implements Extractor{
// The problem is actually here
public <T> List<Matrix<T>> extract(){
List<Matrix<T>> matrixList = new ArrayList<>(); //and here
// "The problem is here!!!!"
matrixList.add(new Matrix<String>("abc"));
}
}
<T>
表示您绑定了一个相对于方法调用的新泛型类型。简而言之,一种新的T
类型仅适用于此方法的执行。您还可以创建List<Matrix<T>>
,然后尝试添加新的Matrix<String>
。如果您知道列表属于Matrix<String>
类型,那么您可以在InfoExtractor
中指定:
//If you cannot generify the interface for some reason
interface Extractor {
public List<? extends Matrix<?>> extract(); //explained at bottom
}
//IDEALLY, then implement Extractor<String> instead
interface Extractor<T> {
public List<Matrix<T>> extract();
}
class InfoExtractor implements Extractor { //or Extractor<String>
public List<Matrix<String>> extract() {
List<Matrix<String>> matrixList = new ArrayList<>();
matrixList.add(new Matrix<>("abc"));
return matrixList;
}
}
当然,您可以看到Extractor
的方法签名已更改。由于在返回类型上使用嵌套泛型,在编译时类型匹配会让事情变得有些混乱。矩阵的<?>
是相当不言自明的,我们在矩阵内部返回多个且可能未知的类型。
通过在? extends Matrix
上指定Extractor
,我们会指定正确的差异。具体泛型是不变,因此List<Toyota>
不是List<Car>
,即使Toyota
是Car
的子类。如果我们希望Matrix
是协变,那么遗憾的是我们还需要Matrix上的边界。这实际上意味着忽略了Liskov的替换主体,我们基本上引用了提取器的具体子类(InfoExtractor ex
vs Extractor ex
),特别是从可用性的角度来看(作为具体类可以返回正确的类型安全,而接口不能)。
当您将列表中的矩阵<T>
指定为类级泛型类型时,这当然会得到更加清晰/干净的处理。
答案 1 :(得分:0)
这取决于你想做什么。
如果您打算使用Matrix来保存单一类型的属性,那就是T,那么最好的方法似乎就是您在编译时进行类型检查时发布的方法。
如果从Matrix中删除泛型类型T,则无法知道每个属性的类型。您可以从Matrix声明中完全删除T,并使用通配符作为Attribute变量。虽然这将允许您在同一Matrix对象中存储不同T类型的属性,但它将删除编译类型检查。换句话说,使用Matrix的类必须知道期望什么类,并且可能将每个属性转换为期望的类。
Class Matrix{
private String name;
List<Attribute<?>> list;
public Matrix(String name, T t){
this.name = name;
list = new ArrayList<>();
list.add(new Attribute<>(t));
}
}
添加一个Class变量并将Class对象保存在属性中,可以帮助您在运行时保留一些信息,以识别属性中对象的类。
class Attribute<T>{
private T attr;
private final Class<T> clazz;
public Attribute(T attr, Class<T> clazz){
this.attr = attr;
this.clazz=clazz;
}
public Attribute(T attr){
this.attr = attr;
Objects.requireNonNull(attr);
this.clazz= (Class<T>) attr.getClass();
}
}
使用这种方法,原始构造函数只能用于非null值,否则类对象应该由程序员提供。