使用interface和Enum进行潜在打字

时间:2015-10-20 15:26:59

标签: java inheritance enums

我有一个IDevice界面和两个这个界面的枚举实现:AndroidDeviceIosDevice。 问题是:我想使用潜在的输入并在接口引用上调用values()方法:

private IDevice getDeviceByReadableName(String versionInXml, IDevice devices) {
    for(IDevice device : devices.values()){
    //...

所以我必须在我的界面添加public IDevice[] values();

public interface IDevice {
     //...
     public IDevice[] values();
}

但它不起作用。 Eclipse要求我remove static modifiers from AndroidDevice(...)

请:

  1. 向我解释为什么它不起作用?
  2. 这个问题的最佳解决方案是什么,我应该使用反射吗?

6 个答案:

答案 0 :(得分:7)

它不起作用的原因是因为为每个枚举隐式定义了静态values()方法。

public enum IOS {

    //Compiler raises an error that values() method is already defined.
    public IOS[] values(){

    }
}

因此,如果已经定义了静态方法,则无法添加具有相同签名的新实例级方法。

解决此问题的最简单方法是使用不同的方法名称。

public interface IDevice {

    IDevice[] allvalues();
}

public enum IOS implements IDevice{

    IPHONE, IPAD;

    @Override
    public IDevice[] allvalues() {
        //return array of iOS devices;
    }
}

答案 1 :(得分:4)

  1. 代码不起作用,因为实现IDevice接口的枚举有values()方法的两个冲突定义,因此您的代码将无法编译。既然你要求提供可靠和官方的参考资料,我会深入研究。首先,请注意枚举隐式创建静态 values()方法(请参阅JVM spec sec. 8.9.2)。但是,您的IDevice接口方法还有一个名为values()实例方法。请注意,当实现接口的类扩展基类时,子类中新定义的方法将覆盖从基类定义的现有实现。因此,IDevice的实例值()方法的实现覆盖了values()方法的现有静态定义(请参阅JVM spec sec. 8.4.8.1)。根据该部分,实例方法覆盖静态方法是编译时错误。
  2. 我不认为反射会有所帮助,因为这是编译时的问题。相反,我建议IDevice改为定义一个类似于values()的方法,比如getValues(),其中getValues()的实现只调用values()。请参阅以下代码段。可以在demo on IDEOne

    中查看完整代码
    enum IosDevice implements IDevice {
        IOS1, IOS2;
    
        public IDevice[] getValues() {
            return IosDevice.values();
        }
    }
    
    enum AndroidDevice implements IDevice {
        AND1, AND2;
    
        public IDevice[] getValues() {
            return AndroidDevice.values();
        }
    }
    
    interface IDevice {
        //...
        public IDevice[] getValues();
    }
    

答案 2 :(得分:4)

使用类型交集:

private <T extends Enum<T> & IDevice> T getDeviceByReadableName(String versionInXml, Class<T> deviceClass) {
    for (T device : deviceClass.getEnumConstants()) {
        if (<some-condition>)
            return device;
    }
    return null;
}

绑定到Enum<T>可确保deviceClass.getEnumConstants()返回值,绑定到& IDevice可确保您获得IDevice

称之为:

enum IosDevice implements IDevice {
    iPhone, iPad, MacBook;
}

IDevice d = getDeviceByReadableName("<foo/>", IosDevice.class);

答案 3 :(得分:4)

你正在从错误的一端接近这个。让enum类代替处理检查:

Java 7

public interface IDevice {
    IDevice getDeviceByReadableName(String versionInXml);
}

public enum AndroidDevice implements IDevice {
    HTC, SAMSUNG;

    @Override
    public IDevice getDeviceByReadableName(String versionInXml) {
        for (IDevice device : values())
            if (...something...)
                return device;

        return null;
    }
}

public enum IosDevice implements IDevice {
    IPHONE, IPAD;

    @Override
    public IDevice getDeviceByReadableName(String versionInXml) {
        for (IDevice device : values())
            if (...something...)
                return device;

        return null;
    }
}

然后:

    private IDevice getDeviceByReadableName(String versionInXml, IDevice devices) {
        return devices.getDeviceByReadableName(versionInXml);
    }

Java 8

如果您使用的是Java 8(幸运的话!),您可以通过一些额外的魔法来实现这一目标:

public interface IDevice {
    Optional<IDevice> getDeviceByReadableName(String versionInXml);

    default Optional<IDevice> getDeviceByReadableName(String versionInXml, Stream<IDevice> devices) {
        return devices.filter((IDevice device) -> {
            return true; // put useful check here
        }).findFirst();
    }
}

public enum AndroidDevice implements IDevice {
    HTC, SAMSUNG;

    @Override
    public Optional<IDevice> getDeviceByReadableName(String versionInXml) {
        return getDeviceByReadableName(versionInXml, Arrays.stream(values()));
    }
}

public enum IosDevice implements IDevice {
    IPHONE, IPAD;

    @Override
    public Optional<IDevice> getDeviceByReadableName(String versionInXml) {
        return getDeviceByReadableName(versionInXml, Arrays.stream(values()));
    }
}

最后:

    private Optional<IDevice> getDeviceByReadableName(String versionInXml, IDevice devices) {
        return devices.getDeviceByReadableName(versionInXml);
    }

答案 4 :(得分:2)

  1. Explain me why it doesn't work?

By having values() declared in your IDevice interface and then an Enum implementing it you won't encounter an issue until compilation.

This is because the compiler will put in a static values() method returning an array of all the values in the enumeration, hence it will complain saying remove the static modifier.

You cannot override the instance method values() defined in IDevice with a static method of the same signature which is what the compiler is essentially trying to do.

  1. What is the best solution of this problem, should I use reflection instead?

You should perhaps just use your IDevice interface as a tag with no methods?

See the following for more info:

http://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.4.8

答案 5 :(得分:1)

如果没有至少看到AndroidDevice中的方法声明,就无法回答这个问题。我假设AndroidDevice#values()方法被标记为static,这在Java中的继承规则之后是不允许的(可能没有OO语言)。这样做的原因是静态意味着该方法“存在”该类,并且您不需要实例来访问该方法。在接口中,您声明了所谓的实例方法,因此您需要一个实例来访问该方法。当您需要操作状态时,需要使用实例方法,您希望通过访问实例值来执行这些操作。您不能静态地(通过类)访问实例方法或状态,因为上下文不会被知道。

因此,如果不知道AndroidDevice代码,我建议您重新考虑一下您希望访问values()方法的方式。恕我直言,静态访问不正确。如果您认为静态访问对您来说是正确的,您可能需要查看Java 8引入的静态接口方法。

但是,只要你有一个状态并想要访问该状态,静态访问就不是正确的面向对象的方式。