Java泛型方法擦除和继承
我遇到了javas泛型和重写方法的问题。想象一下,我有一个深树状的类层次结构。顶级类定义了一个方法foo,该方法接受Strategy类型的1个参数。策略有一个泛型类型参数 我的类层次结构中的每个类都需要重写foo,以限制它可以传递的策略类型,以便策略的泛型类型参数与声明的类匹配。以下是一个例子:Java泛型方法擦除和继承,java,generics,inheritance,type-safety,Java,Generics,Inheritance,Type Safety,我遇到了javas泛型和重写方法的问题。想象一下,我有一个深树状的类层次结构。顶级类定义了一个方法foo,该方法接受Strategy类型的1个参数。策略有一个泛型类型参数 我的类层次结构中的每个类都需要重写foo,以限制它可以传递的策略类型,以便策略的泛型类型参数与声明的类匹配。以下是一个例子: abstract static class TopLevelClass { static final Strategy<TopLevelClass> s1 = tlc ->
abstract static class TopLevelClass {
static final Strategy<TopLevelClass> s1 = tlc -> System.out.println(tlc.getTopLevelAtt());
String getTopLevelAtt() {
return "TopLevelAtt";
}
void foo(Strategy<TopLevelClass> s) {s.bar(this);}
}
static class MidLevelClass extends TopLevelClass {
static final Strategy<MidLevelClass> s2 = mlc -> System.out.println(mlc.getMidLevelAtt());
String getMidLevelAtt() {
return "MidLevelAtt";
}
void foo(Strategy<MidLevelClass> s) {s.bar(this);}
}
static class LowLevelClass extends MidLevelClass {
static final Strategy<LowLevelClass> s3 = llc -> System.out.println(llc.getTopLevelAtt());
String getLowLevelAtt() {
return "LowLevelAtt";
}
void foo(Strategy<LowLevelClass> s) {s.bar(this);}
}
static interface Strategy<X> {
void bar(X x);
}
抽象静态类TopLevelClass{
静态最终策略s1=tlc->System.out.println(tlc.gettoplavelatt());
字符串gettoplavelatt(){
返回“TopLevelAtt”;
}
void foo(策略s){s.bar(此);}
}
静态类MidLevelClass扩展了TopLevelClass{
静态最终策略s2=mlc->System.out.println(mlc.getMidLevelAtt());
字符串getMidLevelAtt(){
返回“MIDLEVEATT”;
}
void foo(策略s){s.bar(此);}
}
静态类LovelClass扩展了MiddlevelClass{
静态最终策略s3=llc->System.out.println(llc.gettoplavelatt());
字符串getLowLevelAtt(){
返回“LowLevelAtt”;
}
void foo(策略s){s.bar(此);}
}
静态接口策略{
空心钢筋(X X);
}
在这个例子中,我希望能够调用LowLevelClass实例上的foo,这些实例具有分别在TopLevelClass、MidLevelClass和LowLevelClass中定义的任何静态引用s1、s2和s3。理想情况下,我不必根据参数调用不同的方法foo1、foo2或foo3
上面的代码不是用java编译的。编译时错误为:
名称冲突:MidLevelClass类型的方法foo(Strategy)与TopLevelClass类型的方法foo(Strategy)具有相同的擦除,但不会覆盖它
我怀疑这个问题很难解决。我可以只使用原始类型并依赖于运行时的类型检查,但我宁愿保持类型安全。如何在不牺牲类型层次结构或类型安全性的情况下实现这一点?请注意,在构造函数中传递策略不是我的选择!必须能够在对象的生命周期内多次调用foo
编辑:
我意识到,在不了解周围环境的情况下,这个问题可能很难理解。我在这里提出了一个更详细的问题,解释了我的问题的背景:如果您担心擦除,那么只需为单独的方法使用单独的方法名即可:
abstract class TopLevelClass {
void fooTop(Strategy<TopLevelClass> s) {/*...*/}
}
class MidLevelClass extends TopLevelClass {
void fooMid(Strategy<MidLevelClass> s) {/*...*/}
}
class LowLevelClass extends MidLevelClass {
void fooLow(Strategy<LowLevelClass> s) {/*...*/}
}
因此,通过方法重写来执行同样的操作是没有意义的。(当然,bar(TopLevelClass)
不能覆盖bar(MidLevelClass)
,尽管从1.5开始就存在协变返回类型,这也是事实。)
向类中添加类型参数以用作方法中的类型参数
abstract class TopLevelClass<T extends TopLevelClass<T>> {
void foo(Strategy<T> s) {/*...*/}
}
class MidLevelClass<T extends MidLevelClass<T>> extends TopLevelClass<T> {
void foo(Strategy<T> s) {/*...*/}
}
class LowLevelClass<T extends LowLevelClass<T>> extends MidLevelClass<T> {
void foo(Strategy<T> s) {/*...*/}
}
静态
字段的类型需要通配符:
static final Strategy<? extends TopLevelClass<?>> s1 =
tlc -> System.out.println(tlc.getTopLevelAtt());
静态最终策略>s1=
tlc->System.out.println(tlc.gettoplavelatt());
(更好的设计更喜欢组合而不是继承。)如果您担心擦除,那么只需为单独的方法使用单独的方法名称:
abstract class TopLevelClass {
void fooTop(Strategy<TopLevelClass> s) {/*...*/}
}
class MidLevelClass extends TopLevelClass {
void fooMid(Strategy<MidLevelClass> s) {/*...*/}
}
class LowLevelClass extends MidLevelClass {
void fooLow(Strategy<LowLevelClass> s) {/*...*/}
}
因此,通过方法重写来执行同样的操作是没有意义的。(当然,bar(TopLevelClass)
不能覆盖bar(MidLevelClass)
,尽管从1.5开始就存在协变返回类型,这也是事实。)
向类中添加类型参数以用作方法中的类型参数
abstract class TopLevelClass<T extends TopLevelClass<T>> {
void foo(Strategy<T> s) {/*...*/}
}
class MidLevelClass<T extends MidLevelClass<T>> extends TopLevelClass<T> {
void foo(Strategy<T> s) {/*...*/}
}
class LowLevelClass<T extends LowLevelClass<T>> extends MidLevelClass<T> {
void foo(Strategy<T> s) {/*...*/}
}
静态
字段的类型需要通配符:
static final Strategy<? extends TopLevelClass<?>> s1 =
tlc -> System.out.println(tlc.getTopLevelAtt());
静态最终策略>s1=
tlc->System.out.println(tlc.gettoplavelatt());
(更好的设计更喜欢组合而不是继承。)我觉得这种设计有几个困难:
策略
,因此不需要Strategy.bar()
来获取该类的实例。参数的传递是妨碍整洁地实现此功能的原因之一foo()
的所有实现都在做完全相同的事情,因此不需要多个实现foo()
方法中传递TopLevelClass
的引用。如果你能想出一个办法,我想那会很棒。在这个解决方案中,类层次结构不是一个因素,因为我们使用的是特定的引用类型
我已经在“更改”开头对更改的部分进行了注释
公共类擦除1{
公共静态void main(字符串[]args){
LowLevelClass slc=new LowLevelClass();//“slc”必须与实例的类型完全相同。这是此解决方案的一个限制。理想情况是此引用的类型为TopLevelClass。
slc.foo(低层类s3,slc);
}
抽象静态类TopLevelClass{
静态最终策略s1=tlc->System.out.println(tlc.gettoplavelatt());
字符串getToLevel(){return“TopLevelAtt”;}
/*更改1:必须传递TopLevelClass子类型的实例,因为
*Strategy.bar()不接受通配符。将其更改为接受“t”以传递给bar()。
*此外,由于现在将“T”作为参数,因此该方法很可能是一个
*实用程序类中的静态方法*/
void foo(策略s、T tlc){
s、 薄层色谱法;
}
}
静态类MidLevelClass扩展了TopLevelClass{
静态最终策略s2=mlc->System.out.println(mlc.getMidLevelAtt());;
字符串getMidLevelAtt(){返回“MidLevelAtt”;}
/*更改2:由于此方法与其父方法没有任何不同,因此不需要这样做*/
//void foo(策略s){s.bar(此);}
}
静态类低级类
public class Erasure1{
public static void main( String[] args ){
LowLevelClass slc = new LowLevelClass(); //'slc' must be exactly the same type as the instance. This is a limitation with this solution. Ideal thing would have been that this reference be of type TopLevelClass.
slc.foo( LowLevelClass.s3, slc );
}
abstract static class TopLevelClass{
static final Strategy<TopLevelClass> s1 = tlc -> System.out.println( tlc.getTopLevelAtt() );
String getTopLevelAtt(){ return "TopLevelAtt"; }
/* CHANGE 1: It is necessary that the instance of TopLevelClass subtype be passed since
* Strategy.bar() doesn't accept wildcards. Changing it to accept a 'T' to pass to bar().
* Also, since, this is now taking 'T' as a parameter, this method could very well be a
* static method in a utility class. */
<T> void foo( Strategy<T> s, T tlc ){
s.bar( tlc );
}
}
static class MidLevelClass extends TopLevelClass{
static final Strategy<MidLevelClass> s2 = mlc -> System.out.println(mlc.getMidLevelAtt());;
String getMidLevelAtt(){ return "MidLevelAtt"; }
/* CHANGE 2: Since this method is not doing anything different from its parent, this is not required. */
//void foo(Strategy<MidLevelClass> s) {s.bar(this);}
}
static class LowLevelClass extends MidLevelClass{
static final Strategy<LowLevelClass> s3 = llc -> System.out.println( llc.getLowLevelAtt() );
String getLowLevelAtt(){ return "LowLevelAtt"; }
/* CHANGE 2: Since this method is not doing anything different from its parent, this is not required. */
//void foo(Strategy<LowLevelClass> s) {s.bar(this);}
}
static interface Strategy<X> {
void bar( X x );
}
}