TL/DR:
有三种方法可以从引用变量的类型中删除类型参数:
- 使用
var
.这只是一个语法速记,var palette = new DiscreteColorMap();
在运行时和编译时都与DiscreteColorMap palette = new DiscreteColorMap();
相同.这一点在另一个答案中得到了回答.
- 使用通配符:
ColorMap<?> palette = new DiscreteColorMap();
.这会告诉编译器"忘记"用作参数类型的类型.这意味着您将无法调用任何需要类型T
的参数的方法,因为编译器无法判断类型是否正确.下面将详细介绍这一点.
- (Don't do this.)使用原始类型:
ColorMap palette = new DiscreteColorMap();
.这将告诉编译器ignore参数的类型(它有效地将T
视为Object
).由于类型不兼容而导致的任何错误都会在运行时引发,并且不会在编译时捕获,因此强烈建议不要使用这种方法.
此答案的其余部分将使用通配符详细描述第二种 Select .
Java泛型的目的是允许灵活地创建可以处理任何类型的对象(或特定类型的对象)的类,同时保留编译器执行编译时类型判断的能力.这方面的典型例子是属于java.util
包的Colltions API.
在您的例子中,您已经定义了一个泛型的ColorMap
接口,我从接口的名称中猜测您正在将参数类型T
的值映射到 colored颜色 .所以你可能会有这样的症状:
public interface ColorMap<T> {
public Color get(T value);
}
然后是一些实现.我将使用非生产级的非常基本的实现,只是为了演示这个 idea .有一个用于Integer
类型的值:
public class DiscreteColorMap implements ColorMap<Integer> {
private final Color[] colors ;
public DiscreteColorMap(Color... colors) {
this.colors = colors ;
}
@Override
public Color get(Integer value) {
return colors[value];
}
}
和一个类型为Double
的:
public class ContinuousColorMap implements ColorMap<Double> {
private final Color start ;
private final Color end ;
public ContinuousColorMap(Color start, Color end) {
this.start = start;
this.end = end;
}
@Override
public Color get(Double value) {
return start.interpolate(end, value);
}
}
请注意,as far as the compiler is concerned、ColorMap<Integer>
和ColorMap<Double>
是不同的类型.
据我所知,您的问题似乎是"我是否可以创建一个DiscreteColorMap
和ContinuousColorMap
,并将它们赋给相同类型的引用".答案是"是的",您可以使用通配符以一种非常重要的方式(即,不只是将它们分配给Object
个引用)来实现这一点:
ColorMap<?> cm1 = new DiscreteColorMap(Color.RED, Color.GREEN, Color.BLUE);
ColorMap<?> cm2 = new ContinuousColorMap(Color.RED, Color.BLUE);
引用类型ColorMap<?>
可以被认为是"某种特定但未知的类型的A ColorMap
".(我认为ColorMap<Integer>
是"Integer
类型的A ColorMap
",以此类推.)
您也可以使用bounded个通配符.由于Integer
和Double
都是Number
的子类,因此可以在类型中指定:
ColorMap<? extends Number> cm1 = new DiscreteColorMap(Color.RED, Color.GREEN, Color.BLUE);
ColorMap<? extends Number> cm2 = new ContinuousColorMap(Color.RED, Color.BLUE);
思考ColorMap<? extends Number>
的方式是"某个特定类型的ColorMap
,即Number
或Number
的子类".这里的Number
是表示参数化类型的upper bound.
使用当前的接口和类定义,这是"统一"两个不同 colored颜色 映射类型的最具体方法:它们都是属于Number
的子类的某种类型的ColorMap
.
这一切是否有用,取决于你想用ColorMap
做什么.你当然可以做到
List<ColorMap<? extends Number>> colorMaps = List.of(cm1, cm2);
这里的问题是,在ColorMap
个consumes个参数化类型的值中,您拥有的唯一方法(即get(...)
方法需要一个类型为T
的参数).因为我们列表中每个ColorMap
的实际类型是未知的(我们只知道它是Number
的specific个子类),所以编译器不能推断我们正在向ColorMap<? extends Number>
的任何给定实例传递正确的值.我们的一个实例特别需要传递一个Integer
,另一个特别需要传递一个Double
.因为没有可以同时包含这两种情况的值,所以我们不能编写任何如下代码:
Number value = 1;
for (ColorMap<? extends Number> cm : colorMaps) {
// this won't compile, because cm expects some specific type of Number:
Color c = cm.get(value);
}
在本例中,下一部分有点做作,但如果ColorMap
有一个produced(即返回)类型T
的值的方法,那么这个列表可能会有用.目前还不清楚您将如何实现这一点,但如果您向接口添加了一个方法:
public interface ColorMap<T> {
public Color get(T value);
public T getValue(Color c);
}
然后您可以执行以下操作:
List<ColorMap<? extends Number>> colorMaps = List.of(cm1, cm2);
Color c = Color.BLUE;
for (ColorMap<? extends Number> cm : colorMaps) {
Number value = cm.getValue(c);
}
这将进行编译.向编译器保证,我们列表中的每个ColorMap
都有一个特定值T
,即Number
或Number
的子类.因此,每个getValue()
方法返回某种类型的Number
,并且保证赋值Number value = cm.getValue(c);
成功.
如果您稍微更改一下ContinuousColorMap
的定义,那么使用公共类型而不使用人工getValue()
方法可能是一种很好的方法:
public interface ColorMap<T> {
public Color get(T value);
}
public class DiscreteColorMap implements ColorMap<Integer> {
private final Color[] colors ;
public DiscreteColorMap(Color... colors) {
this.colors = colors ;
}
@Override
public Color get(Integer value) {
return colors[value];
}
}
这一次,让ContinuousColorMap
变成ColorMap<Number>
:
public class ContinuousColorMap implements ColorMap<Number> {
private final Color start ;
private final Color end ;
public ContinuousColorMap(Color start, Color end) {
this.start = start;
this.end = end;
}
@Override
public Color get(Number value) {
return start.interpolate(end, value.doubleValue());
}
}
现在我们可以做
ColorMap<? super Integer> cm1 = new DiscreteColorMap(Color.RED, Color.GREEN, Color.BLUE);
ColorMap<? super Integer> cm2 = new ContinuousColorMap(Color.RED, Color.BLUE);
List<ColorMap<? super Integer>> colorMaps = List.of(cm1, cm2);
这里,Integer
是表示参数化类型的lower bound,我们可以将ColorMap<? super Integer>
解释为"某个特定类型的AColorMap
,它是Integer
或Integer
的超类".由于Number
是整数的超类,因此将编译cm2
的赋值.
对于列表中的每个元素,get(...)
方法都需要一些特定的类型,但我们知道该特定类型必须是Integer
或Integer
的超类.因此,如果我们通过Integer
,该调用肯定会成功.因此,我们可以做到
Integer value = 1;
for (ColorMap<? super Integer> cm : colorMaps) {
// this line will compile and retrieve the correct color when executed:
Color c = cm.get(value);
}
这可能已经超出了问题的范围,但如果您愿意,您甚至可以编写一个类,根据它们的类型跟踪ColorMap
个实例,并给出一个值Number
,它将从 colored颜色 映射表中返回所提供的特定类型的数字的 colored颜色 .为此,您可以使用Class<T>
类作为"类型令牌":
@SuppressWarnings("unchecked")
public class ColorMaps {
private final Map<Class<? extends Number>, ColorMap<? extends Number>> colorMaps = new HashMap<>();
public <N extends Number> void registerColorMap(Class<N> type, ColorMap<N> map) {
colorMaps.put(type, map);
}
public <N extends Number> ColorMap<N> getColorMap(Class<N> type) {
return (ColorMap<N>) colorMaps.get(type);
}
public <N extends Number> Color getColor(N n) {
Class<N> type = (Class<N>) n.getClass();
return getColorMap(type).get(n);
}
}
然后你可以做一些有趣的事情,比如:
ColorMaps colorMaps = new ColorMaps();
colorMaps.registerColorMap(Integer.class,
new DiscreteColorMap(Color.RED, Color.GREEN, Color.BLUE));
colorMaps.registerColorMap(Double.class,
new ContinuousColorMap(Color.RED, Color.BLUE));
List<Number> numbers = List.of(0, 0.5, 1, 1.0, 2);
for (Number n : numbers) {
System.out.println(n.getClass());
System.out.println(colorMaps.getColor(n));
}
这段代码将使用DiscreteColorMap
来映射列表中的整数(0
、1
和2
),并使用ContinuousColorMap
来映射列表中的双精度数(0.5
和1.0
).