如何解决在这种情况下,如何根据类型选择动作?
我有这样的课程:
abstract class A {
void foo(Object obj) {
pre();
if(obj instanceof Map<String,String[]>) {
methodA();
} else if (obj instanceof Map<String,String>) {
methodB();
} else if (obj instanceof JSONObject) {
...
post();
}
abstract protected void methodA();
abstract protected void methodB();
}
class B extends A { /* Stuff */}
请注意,我有两个不同的地图。但是,让我们关注第一个Map<String,String[]>
。 if语句未编译,因此我尝试了此操作:
interface MyMap extends Map<String,String[]> {}
和
if(obj instanceof MyMap) {
已编译。但是,这似乎不起作用。
Map<String,String[]> map = new Hashtable<>();
B b = new B();
b.foo(map);
这使上面的if语句失败,所以我尝试了这一点:
MyMap map = new Hashtable<>();
但是我得到了:
incompatible types: cannot infer type arguments for java.util.Hashtable<>
并
MyMap map = new Hashtable<String,String[]>();
我明白了:
incompatible types: java.util.Hashtable<java.lang.String,java.lang.String[]> cannot be converted to MyMap
我想在这里实现的是,以前,我为每个对象都有一个单独的方法。但是我不喜欢代码重复,因为pre()
和post()
都相同。我该怎么解决?
解决方法
您对Java有一些误解。
鉴于:
接口MyMap扩展了Map
{}
您已经发表了本体论声明:您似乎认为这意味着:
“我从今开始下令所有Map<String,String[]>
的事物也都是MyMap
。”
或者您可能认为这意味着:
“我已经创建了别名; MyMap
只是Map<String,String[]>
的简写。”
都不是真的。
正确的含义是:
我创建了一个全新的界面,称为MyMap
。任何MyMap都必须是Map MyMap
对象是通过编写new X()
创建的任何对象,其中X是显式具有implements MyMap
的类,或者是执行此操作的类的子类。
因此,在这里尝试解决您的问题完全没有用。
您的第二个误解是泛型。
泛型主要是编译器想象力的体现。 它们是经过编译器检查的注释。说真的去检查:
Map<?,?> = new HashMap<String,String>();
和
Map<?,?> = new HashMap<Integer,Integer>();
同时编译两者,并对结果进行二进制比较。完全相同的字节。因此,可以清楚地证明<String,String>
部分不能幸免于编译。
这称为擦除。泛型出现在可反射访问的内容中,例如方法的返回类型,方法的参数,extends子句中的某些类型或字段的类型-它存储在类文件中,但作为注释(例如,JVM本身甚至不关注它,并且绝对不关心它)。该注释可供javac
查看,并在编译引用这些字段/方法/类的其他代码时使用。
但是其他地方呢? javac使用它来生成警告,错误并以静默方式注入一些强制转换,然后消失。
因此,这个:
x instanceof Map<String,String>
是不可能的。
您只是无法检查。 x
知道它是Map
。不过,不知道泛型是什么。
然后的解决方案是退后一步。
您遇到了一个未知的问题X,并且您想:我知道!我将编写一个接受任何对象的方法,然后检查传入的对象是否为Map<String,String>
,然后解决未知问题X!除非我不知道该如何检查,否则我会询问。
但这是一个死胡同。返回并重新询问“未知问题X”。我很肯定它可以解决,而且很容易解决,但不是这样。
为您提供可能的解决之道
如果您希望消除代码重复,而您尝试删除重复数据的重复代码是以下位置的pre
和post
:
public void someMethod(SomeSpecificType x) {
pre();
customStuffUniqueToThisType();
post();
}
// and 20 more of these,with the same pre and post everywhere
然后也许考虑使用lambda。您可以使用以下方法:
public <T> void apply(T obj,Consumer<T> runner) {
pre();
runner.consume(obj);
post();
}
然后您可以调用它:
Map<String,String> obj = ....;
apply(obj,o -> {
// o is a Map<String,String>.
// do whatever you want here.
});
,
这是一个常见的用例,在面向对象的世界中已经解决了多次。
您要查找的是Template Pattern,Strategy Pattern和Factory pattern的组合。
- 在阅读了Template模式之后,应该清楚您的代码已经在某种程度上使用了该模式。 (尽管:
foo
应该是final
) - 我会让类
A
接受类型参数(即abstract class A<T>
)。我将在class A
中创建一个构造函数,以T作为输入(即A(T t) { this.t = t}
) - 我没有一个名为
methodA
和methodB
的2种方法,而是简单地使用了一个称为doSomething(T t)
的抽象方法。我只会调用一次,并将其传递给t
实例变量。我将删除所有if-else条件。foo
的最终实现将是foo() { pre(); doSomething(t); post() }
然后, - 类
B
可以扩展A
并实现doSomething(T t)
。示例:class B extends A<Map<String,String[]>> { doSomething(<Map<String,String[]>> t) {...do something..} }
- 类似地,我可以创建一个子类,分别用于分别处理
Map<String,String>
和JSONObject
。 - 这时,我有
A
的不同实现。我需要有一个Context
类,该类将传递给A
,它将调用a.foo()
。我需要一个Factory
类,该类创建一个Context
类,并根据某些运行时输入参数将其传递给子类A
的正确实例。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。