How can I create a Java enum with associated values like Swift enum?
Edit: To be clear, I am not asking how to do enums in java. I am asking if there is something in java that complements the associated Swifts values in enums. It's not just how to store values in enums. Take a look at the example I gave and you will see the difference.
So, an iOS developer showed me an architecture where he used transition related enums. I found the concept interesting to me, and as an android developer I'm curious to know if this is possible in Java without being too verbose. What is the equivalent of Java enums? Or is it impossible?
Here is an example of what I mean by associated values. It was pulled from 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).")
}
source to share
For me with Java enums, this won't be possible.
To be closer to your posted snippet you would be as detailed as you already assumed.
enumeration
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 UPCA implementation
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 of 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
source to share
I also wrestled with this question and came up with the following solution. Remarkably, I don't even use enums as Java enums are not suitable for the task. The important part is that for the cases, you want a behavior- switch
like behavior , with the appropriate values available in each case.
enumerated 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
the pros
- Significantly less implementation code than enum based solutions
- Even less code on site use than enum based solutions
against
- A lot of awkward nests in the implementation
Barcode
. It's just that Java is ugly. - You cannot throw exceptions from the actions of a switch statement.
- Corollary of angle brackets in implementation
UPCA
due to multiple arguments. To prevent leakage to the API, you can declarepublic interface UPCAConsumer extends IntFunction<IntFunction<IntFunction<IntConsumer>>> {}
and useUPCAConsumer
for the argument typecaseUPCA(action)
.
source to share