Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I create a Java enum with associated values like Swift enum?

Tags:

java

enums

swift

Edit: To be clear I am not asking how to do enums in java. I am asking if there is something in java that is complementary to Swifts associated values in enums. This is not just simply how do I store values on enums. Take a look at the example I provided and you will see the difference.

So an iOS developer was showing me an architecture where he used swifts enum associated values. This concept seemed interesting to me I and as an android developer I was curious to see if its possible in java without being overly verbose. What is the equivalent with Java enums? Or is it not possible?

Here is an example of what I mean by Associated Values. Its pulled form the apple docs.

enum Barcode {
    case UPCA(Int, Int, Int, Int)
    case QRCode(String)
}

// Instantiated
var productBarcode = Barcode.UPCA(8, 85909, 51226, 3)
// or
productBarcode = .QRCode("ABCDEFGHIJKLMNOP")


switch productBarcode {
case .UPCA(let numberSystem, let manufacturer, let product, let check):
    println("UPC-A: \(numberSystem), \(manufacturer), \(product), \(check).")
case .QRCode(let productCode):
    println("QR code: \(productCode).")
}
like image 434
startoftext Avatar asked May 05 '15 04:05

startoftext


People also ask

Can you give useful examples of enum associated values?

For instance, you might describe a weather enum that lists sunny, windy, and rainy as cases, but has an associated value for cloudy so that you can store the cloud coverage. Or you might describe types of houses, with the number of bedrooms being an associated integer.

How do you create an enum value in Java?

You cannot create an object of an enum explicitly so, you need to add a parameterized constructor to initialize the value(s). The initialization should be done only once. Therefore, the constructor must be declared private or default. To returns the values of the constants using an instance method(getter).

What is associated type in enum?

An enum cannot have both raw values and associated values at the same time. The raw values of an enum must be of the same data type. But associated values can be of any type.

Can associated values and raw values coexist in Swift enumeration?

The Problem with Associated Values We had to do this because Swift doesn't allow us to have both: raw values and associated values within the same enum. A Swift enum can either have raw values or associated values.


2 Answers

For me with Java enums this will not be possible.

To be close to your posted snippet you would be verbose, as you already assume.

enum

enum BarcodeType {
    UPCA,
    QRCode,
    UNDEFINED;
}

factory class

abstract class Barcode {

    abstract public BarcodeType getType();

    public static final Barcode newUPCA(int numberSystem, int manufacturer, int product, int check) {
        return new BarcodeUPCA(numberSystem, manufacturer, product, check);
    }

    public static final Barcode newQRCode(String productCode) {
        return new BarcodeQRCode(productCode);
    }
}

concrete implementation UPCA

class BarcodeUPCA extends Barcode {
    private final int numberSystem;
    private final int manufacturer;
    private final int product;
    private final int check;

    public BarcodeUPCA(int numberSystem, int manufacturer, int product, int check) {
        this.numberSystem = numberSystem;
        this.manufacturer = manufacturer;
        this.product = product;
        this.check = check;
    }

    public int getNumberSystem() {
        return numberSystem;
    }

    public int getManufacturer() {
        return manufacturer;
    }

    public int getProduct() {
        return product;
    }

    public int getCheck() {
        return check;
    }

    @Override
    public BarcodeType getType() {
        return BarcodeType.UPCA;
    }
}

concrete implementation QRCode

class BarcodeQRCode extends Barcode {

    private final String productCode;

    public BarcodeQRCode(String productCode) {
        this.productCode = productCode;
    }

    public String getProductCode() {
        return productCode;
    }

    @Override
    public BarcodeType getType() {
        return BarcodeType.QRCode;
    }
}

demo application

public class BarcodeMain {

    public static void main(String[] args) throws Exception {
        List<Barcode> barcodes = new ArrayList<>();
        barcodes.add(Barcode.newUPCA(8, 85909, 51226, 3));
        barcodes.add(Barcode.newQRCode("foobar"));

        for (Barcode barcode : barcodes) {
            switch (barcode.getType()) {
                case UPCA: {
                    BarcodeUPCA b = (BarcodeUPCA) barcode;
                    System.out.printf("UPC-A: %d, %d, %d, %d%n",
                            b.getNumberSystem(),
                            b.getManufacturer(),
                            b.getProduct(),
                            b.getCheck()
                    );
                    break;
                }
                case QRCode: {
                    BarcodeQRCode b = (BarcodeQRCode) barcode;
                    System.out.printf("QR code: %s%n", b.getProductCode());
                    break;
                }
                default:
                    System.err.println("unhandled type: " + barcode.getType());
            }
        }
}

output

UPC-A: 8, 85909, 51226, 3
QR code: foobar
like image 187
SubOptimal Avatar answered Sep 28 '22 10:09

SubOptimal


I also struggled with this question, and came up with the following solution. Notably, I don't even use enums, since Java's enums are not well-suited to the task. The important part is that you need switch-like behaviour for the cases, with the associated values available in each case.

enum-like type

public abstract class Barcode
{
    private Barcode() {}

    void caseUPCA(IntFunction<IntFunction<IntFunction<IntConsumer>>> action) {}
    void caseQRCode(Consumer<String> action) {}

    static Barcode UPCA(int numberSystem, int manufacturer, int product, int check)
    {
        return new Barcode()
        {
            @Override public void caseUPCA(IntFunction<IntFunction<IntFunction<IntConsumer>>> action)
            {
                action.apply(numberSystem).apply(manufacturer).apply(product).accept(check);
            }
        };
    }

    static Barcode QRCode(String text)
    {
        return new Barcode()
        {
            @Override public void caseQRCode(Consumer<String> action)
            {
                action.accept(text);
            }
        };
    }
}

demo application

public class BarcodeMain
{
    public static void main(String[] args) throws Exception
    {
        List<Barcode> barcodes = new ArrayList<>();
        barcodes.add(Barcode.UPCA(8, 85909, 51226, 3));
        barcodes.add(Barcode.QRCode("foobar"));

        for(Barcode barcode: barcodes)
        {
            barcode.caseUPCA(numberSystem -> manufacturer -> product -> check ->
                System.out.printf("UPC-A: %d, %d, %d, %d%n", numberSystem, manufacturer, product, check));
            barcode.caseQRCode(productCode ->
                System.out.printf("QR code: %s%n", productCode));
        }
    }
}

output

UPC-A: 8, 85909, 51226, 3
QR code: foobar

pros

  • Much less implementation code than enum-based solutions
  • Even less code at usage site than enum-based solutions

cons

  • Lots of awkward nesting in Barcode implementation. Just Java being ugly.
  • You can't throw exceptions from the switch case actions.
  • Angle bracket blindness in UPCA implementation, due to having many arguments. To prevent this leaking into the API, you could declare a public interface UPCAConsumer extends IntFunction<IntFunction<IntFunction<IntConsumer>>> {}, and use UPCAConsumer for the caseUPCA(action) argument type.
like image 42
Will Hains Avatar answered Sep 28 '22 10:09

Will Hains