简体   繁体   English

返回Builder的工厂方法不使用泛型进行编译。 怎么了?

[英]Factory method returning Builder does not compile with generics. What is wrong?

I'm trying to build an API, where a client can register a listener, which receives events. 我正在尝试构建一个API,客户端可以在其中注册接收事件的侦听器。 The listener method that is called upon an event takes an argument. 在事件上调用的侦听器方法带有一个参数。 The API should let the client decide if this argument should be an instance of the concrete class, or an instance of an interface (to avoid unnecessary castings in client listener implementations). API应该让客户端决定该参数是具体类的实例还是接口的实例(以避免在客户端侦听器实现中进行不必要的强制转换)。 To do this I have played around with generics. 为此,我使用了泛型。 It works ok, except for one case! 正常,除了一种情况!

The client implements an interface (see example in MyListener1Impl.java and MyListener2Impl.java) and can with generics decide the argument type of the event. 客户端实现一个接口(请参见MyListener1Impl.java和MyListener2Impl.java中的示例),并且可以使用泛型来确定事件的参数类型。

A static factory method is used to retrieve an instance of ClassABuilder.java. 静态工厂方法用于检索ClassABuilder.java的实例。 The ClassABuilder's build() method returns an instance of ClassA.java ClassABuilder的build()方法返回ClassA.java的实例。

For some reason it does not compile, when a Factory method is used together with the Builder Pattern (calling the methods in telescoping form) and when the concrete listener implementation is used as the generic type argument (see where variable "c4" is set). 由于某种原因,当将Factory方法与Builder模式一起使用(以伸缩形式调用方法)并且将具体的侦听器实现用作泛型类型参数时,它不会编译(请参阅设置变量“ c4”的位置) 。 Can anyone tell me why? 谁能告诉我为什么? What is wrong? 怎么了?

See full example code below. 请参阅下面的完整示例代码。 Copy/paste below classes into Eclipse (or any other IDE) and you'll see where it does not compile. 将以下类复制/粘贴到Eclipse(或任何其他IDE)中,您将看到它无法编译的地方。

class Main.java Main.java类

public class Main{
    public static void main(String[] args){
        // Works ok when generic type argument is set to interface "AnyFile".
        // MyListener2Impl has also declared "AnyFile"
        ClassABuilder<AnyFile> builder0 = Factory.newClassABuilder();
        builder0.set(new MyListener2Impl());
        ClassA<AnyFile> c0 = builder0.build();

        // Also Works ok when methods are called in telescope form 
        // (not sure "telescope" is the correct term?) 
        ClassA<AnyFile> c1 = Factory.newClassABuilder()
        .set(new MyListener2Impl())
        .build();

        // Works ok when generic type argument is set to concrete "MyFileImpl".
        // MyListener1Impl has also declared "MyFileImple"
        ClassABuilder<MyFileImpl> builder2 = Factory.newClassABuilder();
        builder2.set(new MyListener1Impl());
        ClassA<MyFileImpl> c2 = builder2.build();
        // also works ok with telescop form, but Factory class is NOT used.
        ClassA<MyFileImpl> c3 = new ClassABuilder<MyFileImpl>().set(new MyListener1Impl()).build();

        // But with static factory method AND telescope style, it does not compile! Why? What is wrong?
        ClassA<MyFileImpl> c4 = Factory.newClassABuilder()
        .set(new MyListener1Impl())
        .build();
    }
}

class AnyFile.java 类AnyFile.java

import java.util.List;
public interface AnyFile {
    List<AnyFile> listFiles();
}

class ClassA.java ClassA.java类

public class ClassA <T extends AnyFile>{
}

class ClassABuilder.java ClassABuilder.java类

public class ClassABuilder <T extends AnyFile>{
    public ClassABuilder<T> set(Listener<T> a){
        return this;
    }

    public ClassA<T> build(){
        return new ClassA<T>();
    }
}

class Factory.java 类Factory.java

public class Factory {
    public static <T extends AnyFile> ClassABuilder<T> newClassABuilder(){
        return new ClassABuilder<T>();
    }
}

class Listener.java Listener.java类

public interface Listener <T extends AnyFile>{
    void event(T file);
}

class MyFileImpl.java 类MyFileImpl.java

import java.util.List;
public class MyFileImpl implements AnyFile{
    @Override
    public List<AnyFile> listFiles() {
        // do something...
        return null;
    }
}

class MyListener1Impl.java 类MyListener1Impl.java

public class MyListener1Impl implements Listener<MyFileImpl>{
    @Override
    public void event(MyFileImpl file) {
        // do something
    }
}

class MyListener1Impl.java 类MyListener1Impl.java

public class MyListener2Impl implements Listener<AnyFile>{
    @Override
    public void event(AnyFile file) {
        // do something
    }
}

The short answer is that Java's type inference isn't powerful enough to infer left-to-right. 简短的答案是Java的类型推断功能不足以从左到右进行推断。 That is, the type <T> of Factory.newClassABuilder() (which will be used to parameterize ClassA ) must be known before the .set() method can be called, and Java cannot infer Factory.newClassABuilder() 's return type from the argument of a method called on that return type. 也就是说,在调用.set()方法之前,必须知道Factory.newClassABuilder()的类型<T> (将用于参数化ClassA ),并且Java无法推断出Factory.newClassABuilder()的返回类型。从在该返回类型上调用的方法的参数中得出。

This kind of inference requires whole-program analysis, which few languages do, and Java definitely does not support. 这种推断需要整个程序的分析,很少有语言会这样做,而Java绝对不支持。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM