在这种情况下,如何根据类型选择动作?

问题描述

随时改善标题并添加标签。我不知道在这里使用的术语。

我有这样的课程:

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 ,但是反之则不必为true。现在,除了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”。我很肯定它可以解决,而且很容易解决,但不是这样。

为您提供可能的解决之道

如果您希望消除代码重复,而您尝试删除重复数据的重复代码是以下位置的prepost

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 PatternStrategy PatternFactory pattern的组合。

  1. 在阅读了Template模式之后,应该清楚您的代码已经在某种程度上使用了该模式。 (尽管:foo应该是final
  2. 我会让类A接受类型参数(即abstract class A<T>)。我将在class A中创建一个构造函数,以T作为输入(即A(T t) { this.t = t}
  3. 我没有一个名为methodAmethodB的2种方法,而是简单地使用了一个称为doSomething(T t)的抽象方法。我只会调用一次,并将其传递给t实例变量。我将删除所有if-else条件。 foo的最终实现将是foo() { pre(); doSomething(t); post() }
  4. 然后,
  5. B可以扩展A并实现doSomething(T t)。示例:class B extends A<Map<String,String[]>> { doSomething(<Map<String,String[]>> t) {...do something..} }
  6. 类似地,我可以创建一个子类,分别用于分别处理Map<String,String>JSONObject
  7. 这时,我有A的不同实现。我需要有一个Context类,该类将传递给A,它将调用a.foo()。我需要一个Factory类,该类创建一个Context类,并根据某些运行时输入参数将其传递给子类A的正确实例。

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...