Skip to content

Instantly share code, notes, and snippets.

@darichey
Created January 27, 2019 22:54
Show Gist options
  • Save darichey/1124d207ca4bd1376ec777974e7b3a23 to your computer and use it in GitHub Desktop.
Save darichey/1124d207ca4bd1376ec777974e7b3a23 to your computer and use it in GitHub Desktop.
SAM vs real function types and lambda type inference

SAM = Single Abstract Method. It's just another way of saying FunctionalInterface (technically the latter is more correct too because an abstract class with a single abstract method doesnt qualify for a SAM type). So in Java instead of having T -> () we have Consumer<T>. But nothing prevents me from writing an equivalent type:

interface MyDifferentConsumer<T> {
  void consume(T t);
}

This also represents T-> (), it just has a different name. I think the best place we can see how this affects inference is with method references. In java, both of these are perfectly valid:

Consumer<String> c1 = System.out::println;
MyDifferentConsumer<String> c2 = System.out::println;

So then, a question you might ask is, "what is the type of the expression System.out::println? It qualifies for both of these completely disparate types!" The answer is, essentially, it doesn't have one. There are special rules in the JLS for determining if a method reference expression is assignment compatible to any given SAM.

Because System.out::println doesnt really have a type, the following is invalid:

var c1 = System.out::println;
var c2 = System.out::println;

and from my last example, it's easy to see why.

If java had actual function types, all of those special rules wouldnt be necessary and both of those would just infer to String -> ().

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment