3

Say that you have some different classes that does not really have any functionality in common, but somewhere in your program, you need one of them (which one is not necessarily important) because they have a functionality in that context. What is the best way to do that?

I know that you can take an Object and use instanceof to check whether it is one of those types, but I am looking for a way to let the type system ensure that it will just be one of those types. Or is that impractical/impossible?

What I thought about doing was to create an empty interface and implement it in all the classes that needed it. but I have seen that you are supposed to use annotations instead of a marker interface, but I am not sure if that applies in this situation because I do not really care about run-time information. Another thing I am worried about is that implementing that interface in those classes will cause spaghetti code. Those classes should not really have to know anything about how they are used elsewhere.

  • 1
    An empty interface which serves no purpose but type identification is usually referred to as a "marker interface". – Philipp Apr 12 '15 at 12:28
  • 1
    What in the world does an "AppleCarBook" class accomplish? That makes as much sense as a CorrectHorseBatteryStaple. – user22815 Apr 12 '15 at 21:25
  • 1
    @Snowman Apple's 2017 product? It's their take on the electric car powered by the new newer MacBook! – h.j.k. Apr 13 '15 at 3:38
  • @Snowman: Maybe AppleCarBook is not an appropriate name / example, but building the disjoint union of existing types is quite common. I find the empty interface solution OK. – Giorgio Apr 13 '15 at 21:46
  • 1
    @Torm See How do you encode Algebraic Data Types in a C# or Java-like Language?. Tl;dr Base class with private constructor + final/sealed inner subclasses + Visitor pattern (or a method that accepts multiple lambdas, same thing.) – Doval Apr 16 '15 at 16:32
4

Here's a barebones generic implementation for two alternatives:

public abstract class Either<A, B> {
    private Either() { /* prevent outside subclassing */ }

    public static Either<A, B> ofA(A a) {
        return new OptionA<A, B>(a);
    }

    public static Either<A, B> ofB(B b) {
        return new OptionB<A, B>(b);
    }

    public abstract <R> R match(Function<? super A, R> ifA, Function<? super B, R> ifB);

    private static final OptionA<A, B> extends Either<A, B> {
        private final A value;

        private OptionA(A value) { this.value = value; }

        @Override
        public <R> R match(
                Function<? super A, R> ifA,
                Function<? super B, R ifB) {
            return ifA.apply(value);
        }
    }

    private static final OptionB<A, B> extends Either<A, B> {
        private final B value;

        private OptionB(B value) { this.value = value; }

        @Override
        public <R> R match(
                Function<? super A, R> ifA,
                Function<? super B, R> ifB) {
            return ifB.apply(value);
        }
    }
}

You box up objects of one of two types into an Either and then use match to call a function based on which type the boxed object has. The combination of a private constructor and final inner subclasses ensures OptionA and OptionB are the only possible subclasses of Either, so unless you start messing around with casting, if you get your hands on an Either<Integer, String> you know it can only contain an Integer or a String.

It should be fairly straightforward to:

  • Extend this to 3+ alternatives.
  • Add convenience methods like boolean hasA() or A getA() throws NoSuchElementException() for times when match would be too verbose.
  • Use a visitor interface with two methods instead of two individual Functions (but the Functions are more concise if you use lambdas)
  • Choose other names for the alternatives (e.g. Left/Right or First/Second instead of A/B).
  • Add equals, hashCode, and all that stuff.
  • Add null-checking.
  • Add caching in the factory methods.

See also How do you encode Algebraic Data Types in a C#- or Java-like language?.

  • Thank you, that will work nicely. I understand that it might not be possible in Java, but it would be handy to be able to define an Either type for as many arguments that you need. But it seems to me that would require features that the current generics system do not have, so the best thing you can do currently is just make the Either-n classes that you need, even though it feels like copy-pasting code. – Torm Apr 20 '15 at 2:11
2

You cannot meld three classes so that an instance of this chimeric class can be used as an instance of either of the three types (as in LSP).

You can make an umbrella class that, unlike an empty interface, gives you real type-checking.

class AppleBookCar() {
   final Apple apple;
   final Book book;
   final Car car;
   private AppleBookCar(Apple a, Book b, Car c) {
     apple = a; book = b; car = c;
   }
   public static AppleBookCar fromApple(Apple apple) { 
     return AppleBookCar(a, null, null);
   }
   public static AppleBookCar fromBook(Book book) { 
     return AppleBookCar(null, book, null);
   }
   public static AppleBookCar fromCar(Car car) { 
     return AppleBookCar(null, null, car);
   }
   public Apple asApple() { return this.apple; }
   public Book asBook() { return this.book; }
   public Car asCar() { return this.car; }
}

This boilerplate is, of course, boring; better languages would write it for you.

0

Well, all objects extend from the Object class, so maybe you could try to create a generic class and pass in as parameters each of the types. But I am not sure how or why they would all be related because as you said, they have nothing to do with each other. Interesting.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.