Неопределенность конструктора с varargs в java 8

В приведенном ниже классе я получаю ошибку компиляции с Java 8 из-за неоднозначного вызова this(). Тем не менее, с Java 6 этот класс скомпилирован. Я знаю, что могу реорганизовать это с помощью методов factory и т.д., Но для реального класса, где возникает проблема, я бы предпочел поддерживать текущий API на данный момент.

Может ли кто-нибудь подумать о способе устранения двусмысленности без изменения внешнего API?

public class Vararg8 {

    public Vararg8(final Object... os) {}

    public Vararg8(final boolean b,
                   final String s,
                   final int... is) {}

    public Vararg8() {
        this(true, "test", 4, 5, 6);
    }
}

Ответы

Ответ 1

Вы можете сделать это, передав явный массив int[]:

public Vararg8()
{
  this(true, "test", new int[]{4, 5, 6});
}

Вы могли заметить, что это по-прежнему в некотором смысле неоднозначно: то, что вы прошли, по-прежнему совместимо с конструктором Object.... Причина этого в том, что разрешение метода идет в разные этапы, и только последний этап позволяет рассмотреть параметры varargs. Поскольку вы использовали явный массив, он попадает во второй, без необходимости расширения varargs. Он не может попасть в первый, без расширения varargs, так что это не будет рассматриваться до финальной стадии.

Смотрите соответствующие документы JLS:

Первая фаза (§15.12.2.2) выполняет разрешение перегрузки без разрешения преобразования бокса или распаковки или использования вызова метода переменной arity. Если на этом этапе не обнаружен какой-либо применимый метод, обработка продолжается до второй фазы.

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

Третья фаза (§15.12.2.4) позволяет комбинировать перегрузку с методами переменной arity, боксом и распаковкой.

Ответ 2

Попробуйте следующее:

public Vararg8()
{
  this(true, "test", new int[]{4, 5, 6});
}

Ответ 3

Использование явного массива int должно решить вашу проблему.

  public Vararg8() {
        this(true, "test",new int[]{ 4, 5, 6});
    }

Ответ 4

Вы можете использовать generic для вывода типов во время выполнения, но это преобразует их в коробку. Это, конечно, производительность kamikaze, если вы делаете там много арифметики или должны загружать много примитивов, но оставите весь существующий код нормально работающим.

Если вы уже используете типы объектов, это решение не будет стоить вам ничего.

Это выглядело бы так:

public<A extends Boolean, B extends String, C extends Integer> Disambiguate(final A booleanPar,
                                        final B stringPar,
                                        final C... integerPar) {System.out.println("Im in the specific one");}

public<T extends Object> Disambiguate(final T... os) {System.out.println("Im in the general one");}

public static void main(String[] args) {
    new Disambiguate(true, "test", 4, 5, 6);
}

Вы можете использовать generics для "обратной совместимости" с 1,5 и выше и оставить весь существующий код работоспособным и создать новый Api, который позволит избежать проблемы в будущем.

Ответ 5

Массив символов также решает вашу проблему

public Vararg8() {
            this(true, "test".toCharArray(), 4, 5, 6);
        }