Java: перегрузка (универсальная) Методы для java.lang.Number - PullRequest
2 голосов
/ 08 ноября 2011

Итак, я прочитал обсуждение чисел здесь , потому что у меня похожая проблема. В моем случае я хочу разрешить математические операции над числами. Моя идея заключалась в том, чтобы написать неизменный класс «RealNumber», который обрабатывает примитивные числа (целое, длинное, плавающее и двойное) без набора экземпляров элементов управления. Кто-то упомянул методы перегрузки и разрешил компилятору работать.
Это была моя первая попытка:

простой TestClass:

public class Test {
public static void main(String[] args) {
    RealNumber<Double> d = RealNumber.create(3.4);
    d.add(4.7);
    }
}

Класс RealNumber: (пожалуйста, укажите метод с комментарием)

public class RealNumber<N extends Number> extends Number{

    N number;   

    private RealNumber(N number){
        if (number == null){
            throw new NullPointerException("number is null");
        }
        this.number = number;
    }

    public N get(){
        return number;
    }

    //note this Method
    public RealNumber<N> add(N number){
        return add(number);
    }

    private RealNumber<Integer> add(Integer number){
        return new RealNumber<Integer>(intValue() + number);
    }

    private RealNumber<Long> add(Long number){
        return new RealNumber<Long>(longValue() + number);
    }

    private RealNumber<Float> add(Float number){
        return new RealNumber<Float>(floatValue() + number);
    }

    private RealNumber<Double> add(Double number){
        return new RealNumber<Double>(doubleValue() + number);
    }

    @Override
    public int intValue() {
        return number.intValue();
    }

    @Override
    public long longValue() {
        return number.longValue();
    }

    @Override
    public float floatValue() {
        return number.floatValue();
    }

    @Override
    public double doubleValue() {
        return number.doubleValue();
    }


    public static final RealNumber<Integer> create(Integer number){
        return new RealNumber<Integer>(number);
    }

    public static final RealNumber<Long> create(Long number){
        return new RealNumber<Long>(number);
    }

    public static final RealNumber<Float> create(Float number){
        return new RealNumber<Float>(number);
    }

    public static final RealNumber<Double> create(Double number){
        return new RealNumber<Double>(number);
    }
}

поэтому первый тест приводит меня к StackOverflowError, потому что метод «add» всегда вызывает сам себя.

вторая попытка (только измененные методы)

public RealNumber<N> add(Number number){
    return add(number);
}

Во-первых, это не так хорошо, потому что это позволит добавлять BigDecimals или другие вещи, такие как Boolean, а во-вторых, приводит меня к той же StackOverflowError. поэтому я изменился:

public RealNumber<N> add(N number){
    return add(number);
}

//note the public here
public RealNumber<Double> add(Double number){
    return new RealNumber<Double>(doubleValue() + number);
}
//... public RealNumber<Integer, Long, Float> add....

, который не компилируется в моем TestClass -> «Метод add (Double) неоднозначен для типа RealNumber
наконец это сработало:

public RealNumber<N> add(Number number){
    return add(number);
}

//note the public here
public RealNumber<Double> add(Double number){
    return new RealNumber<Double>(doubleValue() + number);
}
//... public RealNumber<Integer, Long, Float> add....

, но приносит еще 2 проблемы: этот шаблон позволяет добавить Doubles к Ints (что приводит к RealNumber of Integer) и приводит к StackOverflowError, если передается BigInteger, Byte или какой-либо другой Number

Итак, мои основные вопросы:

Почему компилятор выбирает правильный метод в Test.class, если каждый add метод является открытым и завершается ошибкой, если он является закрытым.

Что я могу сделать, чтобы исправить проблемы?

Ответы [ 2 ]

2 голосов
/ 08 ноября 2011

The:

public RealNumber<N> add(N number){
        return add(number);
    }

метод всегда вызывает сам себя из-за стирания типа: в Java универсальные шаблоны предназначены только для времени компиляции, они больше не присутствуют во время выполнения, поэтому виртуальная машина фактически не знает, что такое тип N, поэтому она вызывает самые универсальные методы доступно, а именно этот. Вы должны передать тип как Class в качестве аргумента методу aswell, что-то вроде:

public RealNumber<N> add(N number,Class<N> numberClass){
        return add(number);
    }
2 голосов
/ 08 ноября 2011

[Извините, я не до конца понял вопрос с первой попытки. ]

Не думаю, что здесь есть простой ответ @Rafael, потому что, как указал @Andrei Bodnarescu, стирание типа означает, что у вас нет типа вашего параметра N во время выполнения. Я думаю, что вы должны предоставить конкретную реализацию вашего add() метода для каждого подкласса Number.

public RealNumber<Integer> add(Integer number) {
    return new RealNumber<Integer>(intValue() + number);
}
public RealNumber<Long> add(Long number) {
    return new RealNumber<Long>(longValue() + number);
}

Если вы не хотите добавлять целые числа к двойникам, то, я думаю, вам нужно будет сделать что-то вроде:

public RealNumber<Integer> add(Integer number){
    if (!(this instanceof Integer)) {
        throw new IllegalArgumentException("You can't do this...");
    }
    return new RealNumber<Integer>(intValue() + number);
}

Я не вижу простого способа обойти это.

...