Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Multiple type parameters in Java methods, including existing classes and primitive data types

I have a class with code like the following, where I want it to be trivial to use any class/type which represents a number. I find myself defining a large amount of methods, like the following:

public class Range {
    private BigDecimal inferior = new BigDecimal(0);
    private BigDecimal superior = new BigDecimal(1);

    public Range(BigDecimal inferior, BigDecimal superior) {
        if (inferior.compareTo(superior) == -1) {
            this.inferior = inferior;
            this.superior = superior;
        }
    }
    public Range(int inferior, int superior) {
        this(new BigDecimal(inferior), new BigDecimal(superior));
    }
    public Range(Integer inferior, Integer superior) {
        this(new BigDecimal(inferior), new BigDecimal(superior));
    }
    public Range(float inferior, float superior) {
        this(new BigDecimal(inferior), new BigDecimal(superior));
    }
    public Range(double inferior, double superior) {
        this(new BigDecimal(inferior), new BigDecimal(superior));
    }
}

I haven't even written every combination possible! For instance, one which takes a float and a double, or an int and a BigDecimal.

How could this be achieved in a clean way, so that there are parameters valid for multiple classes/data types which are already predefined, or even primitives? I've considered adapters and proxies, but I regularly find myself not understanding the explanations and I can't figure out if they fit my use case and if so how - this question may have already been answered on SO, but if so at least I would like to see if anyone can explain it to me according to this particular example.

like image 412
Xerz Avatar asked Sep 23 '19 22:09

Xerz


People also ask

How many types of primitive data types are there in Java?

The eight primitive data types supported by the Java programming language are: byte: The byte data type is an 8-bit signed two's complement integer. It has a minimum value of -128 and a maximum value of 127 (inclusive).

What is primitive type parameters?

Primitive parameters are parameters of a function that are of primitive types. This means in a function with parameters, any parameter that has type number, string, or boolean is a primitive parameter. When you call a function with parameters, you supply it with arguments.


2 Answers

Use the Builder Pattern. Create a nested static class that accepts each of the distinguishing datatypes for each of the two numbers. Primitive types from byte through long will be widened to long, and float to double. BigIntegers can be converted to BigDecimals, and BigDecimal references will be copied.

public static class Builder {
    BigDecimal first;
    BigDecimal second;

    public void setFirst(long value) { first = new BigDecimal(value); }
    public void setFirst(double value) { first = new BigDecimal(value); }
    public void setFirst(BigInteger value) { first = new BigDecimal(value); }
    public void setFirst(BigDecimal value) { first = value; }
    public void setSecond(long value) { second = new BigDecimal(value); }
    public void setSecond(double value) { second = new BigDecimal(value); }
    public void setSecond(BigInteger value) { second = new BigDecimal(value); }
    public void setSecond(BigDecimal value) { second = value; }
    public Range build() {
        if (first == null || second == null) {
            throw new IllegalArgumentException("Must supply both values.");
        }
        return new Range(first, second);
    }
}

The Builder pattern allows validation before building the desired object, and it bypasses the "constructor explosion" that would occur attempting to cover every possible combination. With n possible types, you have 2 * n builder setter methods instead of n2 constructors.

I included long, even though it can be widened to double legally, for precision reasons, because there are very high values of type long that can't be precisely represented as doubles.

Then, your constructor becomes:

public Range(BigDecimal first, BigDecimal second) {
    if (first.compareTo(second) < 0) {
        this.inferior = first;
        this.superior = second;
    }
    else {
        this.inferior = second;
        this.superior = first;
    }
}

I changed the == -1 to < 0 to match the compareTo contract, and added the else case that switches them if needed.

like image 79
rgettman Avatar answered Nov 07 '22 12:11

rgettman


Use class Number:

public Range(Number inferior, Number superior)

Integer, Long, Double - all of them are subclasses of Number.

Alternatively, use generics:

public class Range<T> {
    private T inferior;
    private T superior;

    public Range(T inferior, T superior) {
        this.inferior = inferior;
        this.superior = superior;
    }
}

Usage:

Range<Long> rangeLong = new Range<>(0L, 1000000000L);
Range<Double> rangeDouble= new Range<>(0d, 457.129d);
like image 25
mentallurg Avatar answered Nov 07 '22 13:11

mentallurg