我如何使这个Java 7兼容?

Vap*_*nus 10 java generics maven java-7 java-8

我有一个基本上看起来像这样的接口:

public interface ISetting<T> {
    public T getDefault();
    public T value();
    public void set(T value);
    public String getName();

    public default String getValueName() {
        Object obj = value();
        if (obj instanceof Boolean) {
            return (boolean)obj ? "Yes" : "No"; 
        }

        return obj.toString();
    }
}
Run Code Online (Sandbox Code Playgroud)

然后在另一个班级我有一个清单 ISetting<?>

private List<ISetting<?>> settings = Arrays.asList(
        new ClassMode(),
        new EndMode(),
        new PlayerLives(),
        new JoinMidGame(),
        new ScoreboardDisplay(),
        new LifePerKill(),
        new ExplosiveBullets(),
        new ReloadTime());
Run Code Online (Sandbox Code Playgroud)

这一切都完美无缺!但是,我使用我的代码的平台不支持Java 8,所以我必须使用Java 7,这就是问题所在.

如果我将Maven目标设置为1.7,就像在我的pom.xml中一样:

<configuration>
    <source>1.8</source>
    <target>1.7</target>
</configuration>
Run Code Online (Sandbox Code Playgroud)

然后代码完美编译,没有错误或任何东西.但是,当我尝试运行代码时,它给了我这个错误:

java.lang.ClassFormatError:类net/uniqraft/murder/match/settings/ISetting中的方法getValueName具有非法修饰符:0x1

我尝试谷歌它但找不到我理解或似乎适用于我的情况.

所以,我想,我只是将整个代码库变成Java 7:

<configuration>
    <source>1.7</source>
    <target>1.7</target>
</configuration>
Run Code Online (Sandbox Code Playgroud)

我看到的第一个错误是:

仅在源级别1.8或更高级别允许默认方法

这令人难以置信的烦恼,我不知道如何绕过它.我的很多代码都依赖于默认实现.我想我只需要使用抽象类代替?

但是我看到的问题更多的是List<Setting<?>>我的错误:

Type mismatch: cannot convert from List<ISetting<? extends Object&Comparable<?>&Serializable>> to List<ISetting<?>>

我不知道这意味着什么或如何解决它.Eclipse提供的quickfix没有任何帮助.

如果您需要查看完整的非剥离ISetting类或完整的堆栈跟踪,我将它们放在外部,因为它们相当宽敞:

And*_*ira 8

我将把答案分为两部分,第一部分是关于类型推断,第二部分是关于默认方法:

类型推断

在Java 7中,无论上下文如何,表达式的类型都是相同的.所以当你这样做时:

Arrays.asList(new ClassMode(), new EndMode(), ...);
Run Code Online (Sandbox Code Playgroud)

它不会创建一个List<ISetting<?>>.您可以通过将settings类型更改为来使其工作List<? extends ISetting<?>>.也就是说,一个列表可以包含可以是它的一个ISetting<?>或任何子类型的元素:

List<? extends ISetting<?>> settings = Arrays.asList(new ClassMode(), new EndMode(), ...);
Run Code Online (Sandbox Code Playgroud)

在Java 8中,List<ISetting<?>>由于多重表达式,将结果列表分配给作品.这意味着某些表达式的推导类型可能受目标类型的影响.所以当你这样做时:

private List<ISetting<?>> settings = Arrays.asList(new ClassMode(), new EndMode(), ...);
Run Code Online (Sandbox Code Playgroud)

编译器分析目标类型并隐式传递一个类型参数Arrays.asList(),这相当于:

private List<ISetting<?>> settings = Arrays.<ISetting<?>>asList(new ClassMode(), new EndMode(), ...);
Run Code Online (Sandbox Code Playgroud)

这创造List<ISetting<?>>并分配给它settings.如果您不想更改settings类型,上面的表单也适用于Java 7 .

默认方法

Java 7没有默认方法.相反,您可以创建一个抽象的骨架实现来与您的界面一起使用.接口将定义类型,骨架实现将提供默认功能.

首先,将default接口中的方法转换为常规方法声明:

public interface ISetting<T> {
    T getDefault();
    T value();
    void set(T value);
    String getName();

    // Former default methods:
    String getValueName();
    boolean isHidden();
    boolean isDefault();
    // etc.
}
Run Code Online (Sandbox Code Playgroud)

然后创建一个抽象类来保存默认实现:

public abstract class AbstractSetting<T> implements ISetting<T> {

    @Override
    public String getValueName() {
        Object obj = value();
        if (obj instanceof Boolean) {
            return ((Boolean) obj) ? "Yes" : "No";
        }
        return obj.toString();
    }

    @Override
    public boolean isHidden() {
        return false;
    }

    // etc.
}
Run Code Online (Sandbox Code Playgroud)

现在让你的具体类实现ISetting<T>接口并扩展AbstractSetting<T>类.例如:

public class ConcreteSetting extends AbstractSetting<Boolean> implements ISetting<Boolean> {
    // concrete implementation
}
Run Code Online (Sandbox Code Playgroud)

  • @VapidLinus很高兴帮忙!是的,您可以省略该部分而不会出现问题.我只是把它放在那里,以便更清楚地说`ConcreteSetting`实现`ISetting <Boolean>`并且扩展`AbstractSetting <Boolean>`只是一个实现细节.这与`ArrayList <T>`声明中使用的样式相同:`class ArrayList <E> extends AbstractList <E>实现List <E>`. (2认同)

Mib*_*bac 5

您需要执行以下操作之一:

  • 从中删除默认修改器 getValueName()
  • make [abstract]类而不是interface
  • 将您的平台升级到Java 1.8