Как я могу сделать этот Java 7 совместимым?

У меня есть интерфейс, который в основном выглядит следующим образом:

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();
    }
}

И затем в другом классе у меня есть список ISetting<?>

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

И все это прекрасно работает! Однако платформа, в которой я использую мой код, не поддерживает Java 8, поэтому я должен использовать Java 7, и здесь, где возникают проблемы.

Если я установил цель Maven в 1.7, как это в моем pom.xml:

<configuration>
    <source>1.8</source>
    <target>1.7</target>
</configuration>

Затем код отлично компилируется без ошибок или чего-то еще. Однако, когда я пытаюсь запустить код, он дает мне эту ошибку:

java.lang.ClassFormatError: метод getValueName в классе net/uniqraft/murder/match/settings/ISetting имеет незаконные модификаторы: 0x1

Я попробовал Google, но не смог найти ничего, что я понял или, казалось, был применим в моем случае.

Итак, я думал, я просто создам всю базу кода в Java 7:

<configuration>
    <source>1.7</source>
    <target>1.7</target>
</configuration>

Первая ошибка, которую я вижу, это:

Способы по умолчанию разрешены только на уровне источника 1,8 или выше

Это невероятно раздражает, и я не знаю, как обойти это. Большая часть моего кода зависит от реализации по умолчанию. Думаю, мне просто нужно использовать абстрактные классы?

Но более проблематичная ошибка, которую я вижу, находится на List<Setting<?>> у меня:

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

Я понятия не имею, что это значит или как это исправить. Предложения quickfix Eclipse не помогают.

В случае, если вам нужно увидеть полный незанятый класс ISetting или полный стек, я поместил их извне, поскольку они довольно пространны:

Ответ 1

Я разберу ответ в двух частях, первый из которых касается вывода типа, а второй - по умолчанию:

Вывод типа

В Java 7 тип выражения тот же, независимо от контекста. Поэтому, когда вы делаете:

Arrays.asList(new ClassMode(), new EndMode(), ...);

Он не создает List<ISetting<?>>. Вы можете заставить его работать, изменив тип settings на List<? extends ISetting<?>>. То есть список, который может содержать элементы, которые могут быть ISetting<?> или любым подтипом:

List<? extends ISetting<?>> settings = Arrays.asList(new ClassMode(), new EndMode(), ...);

В Java 8 присвоение результирующего списка List<ISetting<?>> работает из-за поли выражений. Это означает, что на выведенный тип некоторых выражений может влиять целевой тип. Поэтому, когда вы делаете:

private List<ISetting<?>> settings = Arrays.asList(new ClassMode(), new EndMode(), ...);

Компилятор анализирует целевой тип и неявно передает параметр типа Arrays.asList(), что эквивалентно выполнению:

private List<ISetting<?>> settings = Arrays.<ISetting<?>>asList(new ClassMode(), new EndMode(), ...);

Что создает List<ISetting<?>> и назначает его settings. Вышеупомянутая форма также работает на Java 7, если вы не хотите изменять тип settings.

Методы по умолчанию

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.
}

Затем создайте абстрактный класс для хранения реализаций по умолчанию:

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.
}

Теперь сделайте, чтобы ваши конкретные классы реализовали интерфейс ISetting<T> и расширили класс AbstractSetting<T>. Например:

public class ConcreteSetting extends AbstractSetting<Boolean> implements ISetting<Boolean> {
    // concrete implementation
}

Ответ 2

Вам нужно выполнить одно из следующих действий:

  • удалить модификатор по умолчанию из getValueName()
  • сделать [абстрактный] класс вместо интерфейса
  • обновите свою платформу до Java 1.8