Реализация интерфейса с методом, содержащим неэкспортируемый тип параметра (модули Java 9)

Я использую модульную систему Java 9. Ниже приведен упрощенный вариант моей проблемы.

Я определил ClassA (в модуле com.foo) для реализации InterfaceB (в модуле com.bar). ClassA реализует метод print из InterfaceB, который принимает параметр типа ClassC (в модуле com.baz). Ниже приведен код.

// src/a/com/foo/ClassA.java
package com.foo;

import com.bar.InterfaceB;
import com.baz.ClassC;

public class ClassA implements InterfaceB {
    @Override
    public void print(ClassC obj) {
        System.out.println(obj);
    }
} 


// src/b/com/bar/InterfaceB.java
package com.bar;

import com.baz.ClassC;

public interface InterfaceB {
    public void print(ClassC obj);
}


// src/c/com/baz/ClassC.java
package com.baz;

public class ClassC {
    @Override
    public String toString() {
        return "This is a ClassC object";
    }
}

Модуль com.baz ничего не экспортирует. Итак, чтобы получить доступ к ClassC во время компиляции InterfaceB и ClassA, я использую флаг --add-exports.

InterfaceB успешно компилируется, но когда я пытаюсь скомпилировать ClassA, я получаю сообщение об ошибке:

src/a/com/foo/ClassA.java:6: ClassA is not abstract and does not override abstract method print(ClassC) in InterfaceB

Компилятор каким-то образом использует разные экземпляры ClassC? У меня такое чувство, что с --add-exports происходит что-то неожиданное.

(Кстати, причина, по которой я использую --add-exports, заключается в том, что в моем примере com.baz на самом деле является внутренним пакетом JDK. Я не могу изменить настройки модуля для его экспорта.)


person MattDs17    schedule 02.12.2016    source источник


Ответы (2)


Хотя действительно есть способы заставить модульную систему скомпилировать ваш код (вы уже нашли его), вы столкнетесь с похожими проблемами. во время выполнения, когда, опять же, код, вызывающий InterfaceB::print, не может получить доступ к ClassC. Опять же, для исправления этого можно использовать флаги командной строки. Но они не должны!

Модульная система пытается вам что-то сказать, здесь: "Эта модульность не работает!"

Если любому модулю за пределами com.baz следует разрешить использовать ClassC, тогда пакет, содержащий его, должен быть экспортирован! Именно для этого и нужен экспорт. Другой код явно зависит от ClassC, и система модулей была построена таким образом, чтобы такие зависимости были явными. Поэтому, если вы не делаете это в упражнении, чтобы познакомиться с переопределениями командной строки, реальное решение состоит в том, чтобы com.baz экспортировать com.baz.

(Если не все модули должны его видеть, рассмотрите квалифицированный экспорт Если речь идет об осмыслении, возможно, лучше подходят другие подходы, и следует задать новый вопрос.)

person Nicolai Parlog    schedule 02.12.2016
comment
Я немного упростил свой вопрос. Пакеты, которые не экспортируются, являются частью внутреннего API JDK, поэтому я не могу изменить настройки их модуля для их экспорта. Есть ли лучший способ сделать это? - person MattDs17; 02.12.2016
comment
Нет, проще некуда. Это намеренно сложно, потому что вы не должны этого делать, если на кону не стоят жизни людей. ;) - person Nicolai Parlog; 02.12.2016

Я до сих пор не уверен, почему возникает ошибка, намеренно это или нет, но я нашел решение.

При компиляции ClassA также экспорт модуля com.baz в com.bar (где находится InterfaceB) позволяет компилятору увидеть правильный класс ClassC.

e.g.

-javac ... --add-exports com.baz/com.baz=com.foo,com.bar
person MattDs17    schedule 02.12.2016