With the slight difference that its generics are reified, so all `Function` generic arities have the same name (it's `Function<T>`, `Function<T1, T2>`, ...), although because there is no `void` or `Unit` type in C# it needs two base function types, to handle functions with a return value (`Function`) and functions with no return value (`Action`).
I believe the Kotlin project fixes this issue by having a first-class `Unit` type, so C#'s `Action` becomes `Func<Unit>` in Kotlin.
If they were all just Func<T> (or aliases for that, or wrappers over that when multicast is really needed), it would be a lot simpler.
Function<Void>
It's a bit shady though as you still have to return a value from the body of the method, and that value has to be null.'bit of an understatement there.
I assume there is some way to specify the SAM type explicitly, for cases where it can't be inferred properly?
As to your second question, yes, you can cast a lambda to explicitly specify it's target SAM type:
obj.method((Runnable)(()=> println("oh hai !")));
Another example with 2 args:
The Function* types are the generic structural types able to describe lambdas. C# has them, and so does Scala.Java however decided not to have them, because they don't adhere well with Java's history with nominative types (a type has a name that carries sense). They rather came up with the SAM auto conversion, which is a neat idea in itself, but a bit less flexible than structural types, and may (and will lead to a Cambrian explosion in the number of types, e.g. Reducer, Mapper, Folder, Filter, Predicate, etc.)
What's even worse though is erasure: Java generics are erased at compile time, and this fucks up things really bad with overloading, so in a code like this (in the type List for example):
After erasure, we'll end up with : i.e. 2 methods with the same name and same signature. This is bad. You don't ever want that to happen. And fortunately the java compiler won't let it happen.So, the alternative is I said SAM (Single Abstract Method) types: no structural types, but lambdas can be automatically converted to a SAM if compatible, so you could do this for example:
The lambda gets converted to the Runnable type.