3

None of these compile but ... I would like to express anonymous intersection types in Java such that (A & B) means any type that implements/extends both A and B interfaces, but expressed anonymously in code by composing the types with some syntax like &. In particular I'd like to use them in the following circumstances:

  1. method parameters e.g. public void foo((A & B) x) { ... }
  2. method return types e.g. public (A & B) foo() { ... }
  3. local variables e.g. (A & B) foo = ...
  4. wildcard generics e.g. List<? extends A & B)> foo = ...

Am I right in thinking that the options for me as of now (Java 13) are as follows?

  1. method parameters - use generics: public static <T extends A & B> void foo(T x) { ... }
  2. method return types - use generics: public static <T extends A & B> T foo() { ... }
  3. local variables - use var var foo = (A & B) bar
  4. wildcard generics - no current option

The following articles dig deeper into when/why these types can be useful. This article nicely covers 1. This article covers 1. 2. and 3. This SO question covers 4. and how curious it is that Java has no syntax for this.

--- edited to explain my use case as requested ---

I often use Value objects holding only items of information. These information items are relatively re-usable independently but are often just globbed together for convenience and passed around other classes that apply actual logic with them. I find it's very easy to name getter interfaces in a way that's stable and reusable long term. There's very little debate about what getCustomerForename() and getCustomerId() will return, and defining these is close to defining a "data model" for your business. But once you try and bolt them together to make say a Customer interface, that interface often becomes very special purpose and innappropriately reused as in some contexts you need all the customer info, where in others you only need a few, or with other pieces of data mixed in. It's hard to name, creates coupling whether needn't be any and creates extra code. Instead I want to avoid creating/naming any new interfaces and instead use an anonymous intersection type creating a new type on the fly from existing ones that expose just one getter at a time. So the interfaces A and B in the above would be interfaces such as the following:

interface CustomerId {
  Long getCustomerId();
}

interface CustomerForename {
  String getCustomerForename();
}

... and if I had a report that just surfaced those 2 items, I'd just create objects with the "type" (CustomerId & CustomerForename) instead of naming and creating a CustomerIdAndCustomerForename interface/class for very little benefit.

  • Please explain *in your question* why this would be useful in your specific use case. – Andy Turner Feb 10 '20 at 10:34
  • Are you asking if you 1-4 list of what is possible in regards to intersection types is exhaustive and if there is any option to implement point 4 from your list? – syntagma Feb 10 '20 at 10:46
  • Also, I believe 4 could be possible with reflection but is that also something you are interested in? – syntagma Feb 10 '20 at 10:48
  • Imho the *need* for 4. is just a sign for bad software design. 1., 2., 3. actually as well. This is the kind of stuff that makes (and would make) people write bad code like people who use lambda everywhere for no reason. – akuzminykh Feb 10 '20 at 13:22
  • @AndyTurner Thanks - I've added a bit more context :) – Anthony Leonard Feb 10 '20 at 13:29
  • @syntagma I did partly mean to ask whether there were any more contexts that I was missing! Reflection would be interesting too though, although .... – Anthony Leonard Feb 10 '20 at 13:34
  • @akuzminykh you make a good point - anything too esoteric might work but be so non-idiomatic noone would work with it :/ But I think it's valid to explore the possibilities. I do think that useless special purpose aggregate types that are named as if they offer the perfect `Customer` object but fail to deliver often are also "bad design" – Anthony Leonard Feb 10 '20 at 13:35
  • @AnthonyLeonard Have a look at [this](https://www.baeldung.com/java-decorator-pattern). Maybe this design pattern is something for your case. – akuzminykh Feb 10 '20 at 13:50
  • @akuzminykh one might consider it non-idomatic, but in general intersection types are quite useful and not at all a sign of bad software design. To the contrary it allows to very precisely define what combination of interfaces one requires without tying oneself to much to a specific implementation or falling back to typechecking and casting. – Jens Schauder Feb 10 '20 at 15:01

1 Answers1

1

If you have a type parameter specified as <T extends A&B> you can use it in wildcard types as is done in the following example using Optional<? extends T>. Which is similar, but I guess not the same as what you want to do, although right now my brain hurts too much to explain what the difference is.


public class GenericsTest {

    public static void main(String[] args) {

        System.out.println(generateCombination().get().getB());
        System.out.println(generateCombination().get().getA());
    }

    static <T extends A&B> Optional<? extends T> generateCombination() {
        return (Optional<? extends T>) Optional.of(new Both());
    }

     interface A {
        String getA();
    }

     interface B {
        Number getB();
    }

    static class Both implements A, B {
        @Override
        public String getA() {
            return "Hello";
        }

        @Override
        public Number getB() {
            return 23;
        }
    }
}

Anyway the other variants seem to be correct as described in your question.

Jens Schauder
  • 77,657
  • 34
  • 181
  • 348
  • Thanks Jens much appreciated :) Believe me my head's been hurting all weekend! I will consider more on the Optional approach however so cheers for that. – Anthony Leonard Feb 10 '20 at 16:05