当前位置: 首页 > 面试题库 >

如何用编译时泛型验证替换运行时instanceof check

呼延永新
2023-03-14
问题内容

真正的Java泛型专家有点困惑…;)

假设我有以下两个接口:

interface Processor {
    void process(Foo foo);
}

interface Foo {
    Processor getProcessor();
}

例如,以下两个实现类:

static class SomeProcessor implements Processor {
    static final SomeProcessor INSTANCE = new SomeProcessor();

    @Override
    public void process(Foo foo) {
        if (foo instanceof SomeFoo) { // <-- GET RID OF THIS ?
            // process ((SomeFoo) foo)
        }
    }
}

class SomeFoo implements Foo {
    @Override
    public Processor getProcessor() {
        return SomeProcessor.INSTANCE;
    }
}

有什么方法可以使这两个接口通用,以至于我不需要instanceofprocess()函数中进行标记检查,而在我的代码的其他地方仍可以进行以下构造工作?

foo.getProcessor().process(foo);

(当然,我不知道我在处理哪个Foo子类)

换句话说:我正在寻找一种在对象中定义函数的方法,使其只能返回另一个对象,该对象处理包含该函数的对象的类型。注意:我不仅在谈论处理包含该函数的对象的某些最小公分母超类(上方:Foo),而且还涉及该对象的实际类(上方:SomeFoo)。

(除非我现在真的很愚蠢,否则这听起来似乎微不足道)。


问题答案:

这比我想的还要丑。我的看法:

interface Processor<F extends Foo<F>> {
    void process(F foo);
}

interface Foo<F extends Foo<F>> {
    Processor<F> getProcessor();
}

interface SomeFoo extends Foo<SomeFoo> {
    @Override
    SomeProcessor getProcessor();
}

interface SomeProcessor extends Processor<SomeFoo> {
    @Override
    void process(SomeFoo foo);
}

现在,将编译以下内容:

<F extends Foo<F>> void process(F foo) {
    foo.getProcessor().process(foo);
}

void process(Foo<?> foo) {
    foo.getProcessor().process(foo);
}

不会,因为编译器无法知道所传递的foo的实际类型是其类型参数的子类型,就像有人可以这样写:

    class Bar implements Foo<SomeFoo> { ... }

我们可以通过要求foo的子类型实现对其类型参数的转换来解决此问题:

abstract class Foo<F extends Foo<F>> {
    abstract Processor<F> getProcessor();

    abstract F getThis();
}

class SomeFoo extends Foo<SomeFoo> {
    @Override
    SomeFoo getThis() {
        return this;
    }

    @Override
    Processor<SomeFoo> getProcessor() {
        return new SomeProcessor();
    }
}

现在,我们可以写:

<F extends Foo<F>> void process(Foo<F> foo) {
    foo.getProcessor().process(foo.getThis());
}

并用

Foo<?> foo = ...;
process(foo);

为了易于使用,我建议将helper方法移到Foo类中:

abstract class Foo<F extends Foo<F>> {
    abstract Processor<F> getProcessor();

    abstract F getThis();

    void processWith(Processor<F> p) {
        p.process(getThis());
    }
}

更新:我认为newaccts更新的答案显示了一个更优雅的解决方案,因为它不需要递归类型界限。



 类似资料:
  • 问题内容: 这是我的代码:ExecutorImp扩展了AbstractExecutor,它提取了与实现者相同的执行逻辑(ExecutorImp是一种情况),当调用ExecutorImp的execute()方法时,它将在其父类型中调用该方法,但在父类型中调用该方法(AbstractExcutor )应该知道与实现者绑定的另一个类(在示例中,它是User类): 所以,我的代码有什么问题? 问题答案:

  • Kotlin编译器在这里发出警告(未检查的强制转换): 据我所知,Kotlin smart cast应该知道由检查。 Java代码和编译器给出了完全相同的结果。 为了为每个实现获得正确的util类,我在伴生对象上创建了一个函数,该函数基于泛型类型T的参数为每个实现返回正确的util类,该参数扩展了a:,因此返回类型为。 但是,当我为的每个派生类编写函数体时,使用检查参数的类型,然后使用返回正确的u

  • 问题内容: 在关于反射的本教程中,它指出: […]因为泛型是通过类型擦除实现的,因此在编译过程中会删除有关泛型类型的所有信息 我的知识是使用泛型,以便在编译时编译器可以检查类型安全性。即失败快速方法。但是该链接提到类型擦除会在编译期间删除通用信息。 问题答案: 您引用的语句是正确的:编译器在编译过程中在内部使用通用类型信息,在处理源时会生成与类型相关的错误。然后,一旦完成验证,编译器将生成类型擦除

  • 我在一个类中有一个方法,该方法具有使用泛型指定的返回类型。 对于通用返回类型,我假设上面示例中的返回将评估为: 相反,被正确返回和打印。 如果我将调用更改为: 我错过了什么,以帮助我理解这里发生了什么,为什么原始版本没有导致编译错误。

  • 我想在编译时向某些类添加一个泛型字段。为了实现这个目标,我按照官方文档实现了我自己的 AST 注释和转换类,并使用 AST 注释注释了所需的类。 但是我在编译时得到了这个错误: org . codehaus . groovy . control . multiplecompilationerrorsexception:启动失败:/home/.../groovy/Sample.groovy: -1:

  • 问题内容: 在Java中,可以具有运行时类型(即创建时的类型)和强制类型(您将其强制转换为的类型)。 我想知道这些类型的 专有 名称是什么。例如 创建为,但声明为。引用每个透视图类型的正确方法是什么? 问题答案: 我认为,区分 对象 (存在于执行时,只是具有执行时间类型)和具有编译时类型的 表达式 (例如变量)之间,这一点很重要。 因此,在这种情况下: 是类型的变量。它在执行时的值是对type对象