Why can java not infer a supertypeWhy is lambda return type not checked at compile timeIs Java “pass-by-reference” or “pass-by-value”?What is a serialVersionUID and why should I use it?How do I read / convert an InputStream into a String in Java?When to use LinkedList over ArrayList in Java?How do I generate random integers within a specific range in Java?Creating a memory leak with JavaWhy is subtracting these two times (in 1927) giving a strange result?Why don't Java's +=, -=, *=, /= compound assignment operators require casting?Why is char[] preferred over String for passwords?Why is processing a sorted array faster than processing an unsorted array?
What does "speed checked" mean?
Precious Stone, as Clear as Diamond
Why combine commands on a single line in a Bash script?
What does a single quote inside a C# date time format mean?
Are there any spells that aren't on any class's spell list?
Migrate foreign key type from char to binary - ways to deal with the fallout?
How does an aircraft descend without its nose pointing down?
Why is the past tense of vomit generally spelled 'vomited' rather than 'vomitted'?
Travel with Expired Greek Passport from UK to Greece and Return
Are the Properties of the EM Spectrum Fluid?
Alternatives to boxes
What are some examples of three-mora atamadaka verbs besides 帰る?
Why is the Falcon Heavy center core recovery done at sea?
Explanation of tac --before
Why does the passage the Letwin amendment "scrap the possibility of a meaningful vote on Johnson's deal"?
C function to check the validity of a date in DD.MM.YYYY format
More optional xx_use:N and xx_new:N functions than documented?
Multiple devices with one IPv6 to the Internet?
On the finite simple groups with an irreducible complex representation of a given dimension
Why is my Windows 7 recovery folder 53% of my disk
What is a word for the feeling of constantly wanting new possessions?
Is it possible to have a healthy work-life balance as a professor?
What would be the best propulsion system for this aircraft carrier?
Can someone help explain what this FFT workflow is doing to my signal, and why it works?
Why can java not infer a supertype
Why is lambda return type not checked at compile timeIs Java “pass-by-reference” or “pass-by-value”?What is a serialVersionUID and why should I use it?How do I read / convert an InputStream into a String in Java?When to use LinkedList over ArrayList in Java?How do I generate random integers within a specific range in Java?Creating a memory leak with JavaWhy is subtracting these two times (in 1927) giving a strange result?Why don't Java's +=, -=, *=, /= compound assignment operators require casting?Why is char[] preferred over String for passwords?Why is processing a sorted array faster than processing an unsorted array?
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty
margin-bottom:0;
We all know Long extends Number. So why does this not compile. And how to define the method "with" so that the program compiles without any manual cast.
import java.util.function.Function;
public class Builder<T>
static public interface MyInterface
Number getNumber();
Long getLong();
public <F extends Function<T, R>, R> Builder<T> with(F getter, R returnValue)
return null;//TODO
public static void main(String[] args)
// works:
new Builder<MyInterface>().with(MyInterface::getLong, 4L);
// works:
new Builder<MyInterface>().with(MyInterface::getNumber, (Number) 4L);
// works:
new Builder<MyInterface>().<Function<MyInterface, Number>, Number> with(MyInterface::getNumber, 4L);
// works:
new Builder<MyInterface>().with((Function<MyInterface, Number>) MyInterface::getNumber, 4L);
// compilation error: Cannot infer ...
new Builder<MyInterface>().with(MyInterface::getNumber, 4L);
// compilation error: Cannot infer ...
new Builder<MyInterface>().with(MyInterface::getNumber, Long.valueOf(4));
// compiles but also involves typecast (and Casting Number to Long is not even safe):
new Builder<MyInterface>().with( myInterface->(Long) myInterface.getNumber(), 4L);
// compiles but also involves manual conversion:
new Builder<MyInterface>().with(myInterface -> myInterface.getNumber().longValue(), 4L);
// compiles (compiler you are kidding me?):
new Builder<MyInterface>().with(castToFunction(MyInterface::getNumber), 4L);
static <X, Y> Function<X, Y> castToFunction(Function<X, Y> f)
return f;
- Cannot infer type argument(s) for
<F, R> with(F, R)
- The type of getNumber() from the type Builder.MyInterface is Number, this is incompatible with the descriptor's return type: Long
For usecase see: Why is lambda return type not checked at compile time
java type-inference
|
show 1 more comment
We all know Long extends Number. So why does this not compile. And how to define the method "with" so that the program compiles without any manual cast.
import java.util.function.Function;
public class Builder<T>
static public interface MyInterface
Number getNumber();
Long getLong();
public <F extends Function<T, R>, R> Builder<T> with(F getter, R returnValue)
return null;//TODO
public static void main(String[] args)
// works:
new Builder<MyInterface>().with(MyInterface::getLong, 4L);
// works:
new Builder<MyInterface>().with(MyInterface::getNumber, (Number) 4L);
// works:
new Builder<MyInterface>().<Function<MyInterface, Number>, Number> with(MyInterface::getNumber, 4L);
// works:
new Builder<MyInterface>().with((Function<MyInterface, Number>) MyInterface::getNumber, 4L);
// compilation error: Cannot infer ...
new Builder<MyInterface>().with(MyInterface::getNumber, 4L);
// compilation error: Cannot infer ...
new Builder<MyInterface>().with(MyInterface::getNumber, Long.valueOf(4));
// compiles but also involves typecast (and Casting Number to Long is not even safe):
new Builder<MyInterface>().with( myInterface->(Long) myInterface.getNumber(), 4L);
// compiles but also involves manual conversion:
new Builder<MyInterface>().with(myInterface -> myInterface.getNumber().longValue(), 4L);
// compiles (compiler you are kidding me?):
new Builder<MyInterface>().with(castToFunction(MyInterface::getNumber), 4L);
static <X, Y> Function<X, Y> castToFunction(Function<X, Y> f)
return f;
- Cannot infer type argument(s) for
<F, R> with(F, R)
- The type of getNumber() from the type Builder.MyInterface is Number, this is incompatible with the descriptor's return type: Long
For usecase see: Why is lambda return type not checked at compile time
java type-inference
Can you postMyInterface
?
– Maurice Perry
Oct 14 at 13:42
its already inside the class
– jukzi
Oct 14 at 13:42
Hmm I tried<F extends Function<T, R>, R, S extends R> Builder<T> with(F getter, S returnValue)
but gotjava.lang.Number cannot be converted to java.lang.Long)
, which is surprising because I don't see where the compiler gets the idea that the return value fromgetter
will need to be converted toreturnValue
.
– jingx
Oct 14 at 14:56
@jukzi OK. Sorry, I missed that.
– Maurice Perry
Oct 15 at 4:58
ChangingNumber getNumber()
to<A extends Number> A getNumber()
makes stuff work. No idea whether this is what you wanted. As others have said the issue is thatMyInterface::getNumber
could be a function that returnsDouble
for example and notLong
. Your declaration does not allow the compiler to narrow the return type based on other information present. By using a generic return type you allow the compiler to do so, hence it works.
– Giacomo Alzetta
Oct 15 at 9:19
|
show 1 more comment
We all know Long extends Number. So why does this not compile. And how to define the method "with" so that the program compiles without any manual cast.
import java.util.function.Function;
public class Builder<T>
static public interface MyInterface
Number getNumber();
Long getLong();
public <F extends Function<T, R>, R> Builder<T> with(F getter, R returnValue)
return null;//TODO
public static void main(String[] args)
// works:
new Builder<MyInterface>().with(MyInterface::getLong, 4L);
// works:
new Builder<MyInterface>().with(MyInterface::getNumber, (Number) 4L);
// works:
new Builder<MyInterface>().<Function<MyInterface, Number>, Number> with(MyInterface::getNumber, 4L);
// works:
new Builder<MyInterface>().with((Function<MyInterface, Number>) MyInterface::getNumber, 4L);
// compilation error: Cannot infer ...
new Builder<MyInterface>().with(MyInterface::getNumber, 4L);
// compilation error: Cannot infer ...
new Builder<MyInterface>().with(MyInterface::getNumber, Long.valueOf(4));
// compiles but also involves typecast (and Casting Number to Long is not even safe):
new Builder<MyInterface>().with( myInterface->(Long) myInterface.getNumber(), 4L);
// compiles but also involves manual conversion:
new Builder<MyInterface>().with(myInterface -> myInterface.getNumber().longValue(), 4L);
// compiles (compiler you are kidding me?):
new Builder<MyInterface>().with(castToFunction(MyInterface::getNumber), 4L);
static <X, Y> Function<X, Y> castToFunction(Function<X, Y> f)
return f;
- Cannot infer type argument(s) for
<F, R> with(F, R)
- The type of getNumber() from the type Builder.MyInterface is Number, this is incompatible with the descriptor's return type: Long
For usecase see: Why is lambda return type not checked at compile time
java type-inference
We all know Long extends Number. So why does this not compile. And how to define the method "with" so that the program compiles without any manual cast.
import java.util.function.Function;
public class Builder<T>
static public interface MyInterface
Number getNumber();
Long getLong();
public <F extends Function<T, R>, R> Builder<T> with(F getter, R returnValue)
return null;//TODO
public static void main(String[] args)
// works:
new Builder<MyInterface>().with(MyInterface::getLong, 4L);
// works:
new Builder<MyInterface>().with(MyInterface::getNumber, (Number) 4L);
// works:
new Builder<MyInterface>().<Function<MyInterface, Number>, Number> with(MyInterface::getNumber, 4L);
// works:
new Builder<MyInterface>().with((Function<MyInterface, Number>) MyInterface::getNumber, 4L);
// compilation error: Cannot infer ...
new Builder<MyInterface>().with(MyInterface::getNumber, 4L);
// compilation error: Cannot infer ...
new Builder<MyInterface>().with(MyInterface::getNumber, Long.valueOf(4));
// compiles but also involves typecast (and Casting Number to Long is not even safe):
new Builder<MyInterface>().with( myInterface->(Long) myInterface.getNumber(), 4L);
// compiles but also involves manual conversion:
new Builder<MyInterface>().with(myInterface -> myInterface.getNumber().longValue(), 4L);
// compiles (compiler you are kidding me?):
new Builder<MyInterface>().with(castToFunction(MyInterface::getNumber), 4L);
static <X, Y> Function<X, Y> castToFunction(Function<X, Y> f)
return f;
- Cannot infer type argument(s) for
<F, R> with(F, R)
- The type of getNumber() from the type Builder.MyInterface is Number, this is incompatible with the descriptor's return type: Long
For usecase see: Why is lambda return type not checked at compile time
java type-inference
java type-inference
edited Oct 14 at 16:12
jukzi
asked Oct 14 at 13:38
jukzijukzi
3792 silver badges14 bronze badges
3792 silver badges14 bronze badges
Can you postMyInterface
?
– Maurice Perry
Oct 14 at 13:42
its already inside the class
– jukzi
Oct 14 at 13:42
Hmm I tried<F extends Function<T, R>, R, S extends R> Builder<T> with(F getter, S returnValue)
but gotjava.lang.Number cannot be converted to java.lang.Long)
, which is surprising because I don't see where the compiler gets the idea that the return value fromgetter
will need to be converted toreturnValue
.
– jingx
Oct 14 at 14:56
@jukzi OK. Sorry, I missed that.
– Maurice Perry
Oct 15 at 4:58
ChangingNumber getNumber()
to<A extends Number> A getNumber()
makes stuff work. No idea whether this is what you wanted. As others have said the issue is thatMyInterface::getNumber
could be a function that returnsDouble
for example and notLong
. Your declaration does not allow the compiler to narrow the return type based on other information present. By using a generic return type you allow the compiler to do so, hence it works.
– Giacomo Alzetta
Oct 15 at 9:19
|
show 1 more comment
Can you postMyInterface
?
– Maurice Perry
Oct 14 at 13:42
its already inside the class
– jukzi
Oct 14 at 13:42
Hmm I tried<F extends Function<T, R>, R, S extends R> Builder<T> with(F getter, S returnValue)
but gotjava.lang.Number cannot be converted to java.lang.Long)
, which is surprising because I don't see where the compiler gets the idea that the return value fromgetter
will need to be converted toreturnValue
.
– jingx
Oct 14 at 14:56
@jukzi OK. Sorry, I missed that.
– Maurice Perry
Oct 15 at 4:58
ChangingNumber getNumber()
to<A extends Number> A getNumber()
makes stuff work. No idea whether this is what you wanted. As others have said the issue is thatMyInterface::getNumber
could be a function that returnsDouble
for example and notLong
. Your declaration does not allow the compiler to narrow the return type based on other information present. By using a generic return type you allow the compiler to do so, hence it works.
– Giacomo Alzetta
Oct 15 at 9:19
Can you post
MyInterface
?– Maurice Perry
Oct 14 at 13:42
Can you post
MyInterface
?– Maurice Perry
Oct 14 at 13:42
its already inside the class
– jukzi
Oct 14 at 13:42
its already inside the class
– jukzi
Oct 14 at 13:42
Hmm I tried
<F extends Function<T, R>, R, S extends R> Builder<T> with(F getter, S returnValue)
but got java.lang.Number cannot be converted to java.lang.Long)
, which is surprising because I don't see where the compiler gets the idea that the return value from getter
will need to be converted to returnValue
.– jingx
Oct 14 at 14:56
Hmm I tried
<F extends Function<T, R>, R, S extends R> Builder<T> with(F getter, S returnValue)
but got java.lang.Number cannot be converted to java.lang.Long)
, which is surprising because I don't see where the compiler gets the idea that the return value from getter
will need to be converted to returnValue
.– jingx
Oct 14 at 14:56
@jukzi OK. Sorry, I missed that.
– Maurice Perry
Oct 15 at 4:58
@jukzi OK. Sorry, I missed that.
– Maurice Perry
Oct 15 at 4:58
Changing
Number getNumber()
to <A extends Number> A getNumber()
makes stuff work. No idea whether this is what you wanted. As others have said the issue is that MyInterface::getNumber
could be a function that returns Double
for example and not Long
. Your declaration does not allow the compiler to narrow the return type based on other information present. By using a generic return type you allow the compiler to do so, hence it works.– Giacomo Alzetta
Oct 15 at 9:19
Changing
Number getNumber()
to <A extends Number> A getNumber()
makes stuff work. No idea whether this is what you wanted. As others have said the issue is that MyInterface::getNumber
could be a function that returns Double
for example and not Long
. Your declaration does not allow the compiler to narrow the return type based on other information present. By using a generic return type you allow the compiler to do so, hence it works.– Giacomo Alzetta
Oct 15 at 9:19
|
show 1 more comment
6 Answers
6
active
oldest
votes
This expression :
new Builder<MyInterface>().with(MyInterface::getNumber, 4L);
can be rewritten as :
new Builder<MyInterface>().with(myInterface -> myInterface.getNumber(), 4L);
Taking into account method signature :
public <F extends Function<T, R>, R> Builder<T> with(F getter, R returnValue)
R
will be inferred toLong
F
will beFunction<MyInterface, Long>
and you pass a method reference which will be infered as Function<MyInterface, Number>
This is the key - how should compiler predict that you actually want to return Long
from a function with such signature? It will not do the downcasting for you.
Since Number
is superclass of Long
and Number
is not necessairly a Long
(this is why it does not compile) - you would have to cast explicitly on your own :
new Builder<MyInterface>().with(myInterface -> (Long) myInterface.getNumber(), 4L);
making F
to be Function<MyIinterface, Long>
or pass generic arguments explicitly during method call as you did :
new Builder<MyInterface>().<Function<MyInterface, Number>, Number> with(MyInterface::getNumber, 4L);
and know R
will be seen as Number
and code will compile.
Thats an interesting typecast. But still i am searching for a definition of the method "with" which will make the caller compile without any cast. Anyway thanks for that idea.
– jukzi
Oct 14 at 14:12
@jukzi You can't. It does not matter how yourwith
is written. You haveMJyInterface::getNumber
has typeFunction<MyInterface, Number>
soR=Number
and then you also haveR=Long
from the other argument (remember that Java literals are not polymorphic!). At this point the compiler stops because it's not always possible to convert aNumber
to aLong
. The only way to fix this is to changeMyInterface
to use<A extends Number> Number
as return type, this makes the compiler haveR=A
and thenR=Long
and sinceA extends Number
it can substituteA=Long
– Giacomo Alzetta
Oct 15 at 9:27
add a comment
|
The key to your error is in the generic declaration of the type of F
: F extends Function<T, R>
. The statement that does not work is: new Builder<MyInterface>().with(MyInterface::getNumber, 4L);
First, you have a new Builder<MyInterface>
. The declaration of the class therefore implies T = MyInterface
. As per your declaration of with
, F
must be a Function<T, R>
, which is a Function<MyInterface, R>
in this situation. Therefore, the parameter getter
must take a MyInterface
as parameter (satisfied by the method references MyInterface::getNumber
and MyInterface::getLong
), and return R
, which must be the same type as the second parameter to the function with
. Now, let's see if this holds for all of your cases:
// T = MyInterface, F = Function<MyInterface, Long>, R = Long
new Builder<MyInterface>().with(MyInterface::getLong, 4L);
// T = MyInterface, F = Function<MyInterface, Number>, R = Number
// 4L explicitly widened to Number
new Builder<MyInterface>().with(MyInterface::getNumber, (Number) 4L);
// T = MyInterface, F = Function<MyInterface, Number>, R = Number
// 4L implicitly widened to Number
new Builder<MyInterface>().<Function<MyInterface, Number>, Number>with(MyInterface::getNumber, 4L);
// T = MyInterface, F = Function<MyInterface, Number>, R = Number
// 4L implicitly widened to Number
new Builder<MyInterface>().with((Function<MyInterface, Number>) MyInterface::getNumber, 4L);
// T = MyInterface, F = Function<MyInterface, Number>, R = Long
// F = Function<T, not R> violates definition, therefore compilation error occurs
// Compiler cannot infer type of method reference and 4L at the same time,
// so it keeps the type of 4L as Long and attempts to infer a match for MyInterface::getNumber,
// only to find that the types don't match up
new Builder<MyInterface>().with(MyInterface::getNumber, 4L);
You can "fix" this problem with the following options:
// stick to Long
new Builder<MyInterface>().with(MyInterface::getLong, 4L);
// stick to Number
new Builder<MyInterface>().with(MyInterface::getNumber, (Number) 4L);
// explicitly convert the result of getNumber:
new Builder<MyInterface>().with(myInstance -> (Long) myInstance.getNumber(), 4L);
// explicitly convert the result of getLong:
new Builder<MyInterface>().with(myInterface -> (Number) myInterface.getLong(), (Number) 4L);
Beyond this point, it's mostly a design decision for which option reduces code complexity for your particular application, so choose whatever fits you best.
The reason that you cannot do this without casting lies in the following, from the Java Language Specification:
Boxing conversion treats expressions of a primitive type as expressions of a corresponding reference type. Specifically, the following nine conversions are called the boxing conversions:
- From type boolean to type Boolean
- From type byte to type Byte
- From type short to type Short
- From type char to type Character
- From type int to type Integer
- From type long to type Long
- From type float to type Float
- From type double to type Double
- From the null type to the null type
As you can clearly see, there is no implicit boxing conversion from long to Number, and the widening conversion from Long to Number can only occur when the compiler is sure that it requires a Number and not a Long. As there is a conflict between the method reference that requires a Number and the 4L that provides a Long, the compiler (for some reason???) unable to make the logical leap that Long is-a Number and deduce that F
is a Function<MyInterface, Number>
.
Instead, I managed to resolve the problem by slightly editing the function signature:
public <R> Builder<T> with(Function<T, ? super R> getter, R returnValue)
return null;//TODO
After this change, the following occurs:
// doesn't work, as it should not work
new Builder<MyInterface>().with(MyInterface::getLong, (Number), 4L);
// works, as it always did
new Builder<MyInterface>().with(MyInterface::getLong, 4L);
// works, as it should work
new Builder<MyInterface>().with(MyInterface::getNumber, (Number)4L);
// works, as you wanted
new Builder<MyInterface>().with(MyInterface::getNumber, 4L);
Edit:
After spending some more time on it, it's annoyingly difficult to enforce getter-based type safety. Here's a working example that uses setter methods to enforce a builder's type-safety:
public class Builder<T>
static public interface MyInterface
//setters
void number(Number number);
void Long(Long Long);
void string(String string);
//getters
Number number();
Long Long();
String string();
// whatever object we're building, let's say it's just a MyInterface for now...
private T buildee = (T) new MyInterface()
private String string;
private Long Long;
private Number number;
public void number(Number number)
this.number = number;
public void Long(Long Long)
this.Long = Long;
public void string(String string)
this.string = string;
public Number number()
return this.number;
public Long Long()
return this.Long;
public String string()
return this.string;
;
public <R> Builder<T> with(BiConsumer<T, R> setter, R val)
setter.accept(this.buildee, val); // take the buildee, and set the appropriate value
return this;
public static void main(String[] args)
// works:
new Builder<MyInterface>().with(MyInterface::Long, 4L);
// works:
new Builder<MyInterface>().with(MyInterface::number, (Number) 4L);
// compile time error, as it shouldn't work
new Builder<MyInterface>().with(MyInterface::Long, (Number) 4L);
// works, as it always did
new Builder<MyInterface>().with(MyInterface::Long, 4L);
// works, as it should
new Builder<MyInterface>().with(MyInterface::number, (Number)4L);
// works, as you wanted
new Builder<MyInterface>().with(MyInterface::number, 4L);
// compile time error, as you wanted
new Builder<MyInterface>().with(MyInterface::number, "blah");
Provided the type-safe ability to construct an object, hopefully at some point in the future we'll be able to return an immutable data object from the builder (maybe by adding a toRecord()
method to the interface, and specifying the builder as a Builder<IntermediaryInterfaceType, RecordType>
), so you don't even have to worry about the resulting object being modified. Honestly, it's an absolute shame that it requires so much effort to get a type-safe field-flexible builder, but it's probably impossible without some new features, code generation, or an annoying amount of reflection.
Thanks for all your work but i do not see any improvement towards avoiding the manual typecast. My naive understanding is that a compiler should be able to infer (i.e. typecast) everything that a human can.
– jukzi
Oct 14 at 14:24
@jukzi My naive understanding is the same, but for whatever reason it doesn't work that way. I've come up with a workaround that pretty much achieves the desired effect, though
– Avi
Oct 14 at 14:39
Thanks again. But your new proposal is too wide (see stackoverflow.com/questions/58337639) since it allows compilation of ".with(MyInterface::getNumber, "I AM NOT A NUMBER")";
– jukzi
Oct 14 at 14:42
your sentence "Compiler cannot infer type of method reference and 4L at the same time" is cool. But i just want it the other way arround. the compiler should try Number based on the first parameter and do a widening to Number of the second parameter.
– jukzi
Oct 14 at 14:45
1
WHAAAAAAAAAAAT? Why does BiConsumer work as intended while Function does not? I do not get the idea. I admit this is exactly the typesafety i wanted but unfortunatlly does not work with getters. WHY WHY WHY.
– jukzi
Oct 14 at 16:22
|
show 5 more comments
It seems the compiler has used the value 4L to decide that R is Long, and getNumber() returns a Number, which is not necessarily a Long.
But I'm not sure why the value takes precedence over the method...
add a comment
|
The Java compiler is in general not good at inferring multiple/nested generic types or wildcards. Often I cannot get something to compile without using a helper function to capture or infer some of the types.
But, do you really need to capture the exact type of Function
as F
? If not, maybe the following works, and as you can see, also seems to work with subtypes of Function
.
import java.util.function.Function;
import java.util.function.UnaryOperator;
public class Builder<T>
public interface MyInterface
Number getNumber();
Long getLong();
public <R> Builder<T> with(Function<T, R> getter, R returnValue)
return null;
// example subclass of Function
private static UnaryOperator<String> stringFunc = (s) -> (s + ".");
public static void main(String[] args)
// works
new Builder<MyInterface>().with(MyInterface::getNumber, 4L);
// works
new Builder<String>().with(stringFunc, "s");
"with(MyInterface::getNumber, "NOT A NUMBER")" should not compile
– jukzi
Oct 15 at 8:20
add a comment
|
The most interesting part lies in the difference between those 2 lines, I think:
// works:
new Builder<MyInterface>().<Function<MyInterface, Number>, Number> with(MyInterface::getNumber, 4L);
// compilation error: Cannot infer ...
new Builder<MyInterface>().with(MyInterface::getNumber, 4L);
In the first case, the T
is explicitly Number
, so 4L
is also a Number
, no problem. In the second case, 4L
is a Long
, so T
is a Long
, so your function is not compatible, and Java cannot know if you meant Number
or Long
.
add a comment
|
With the following signature:
public <R> Test<T> with(Function<T, ? super R> getter, R returnValue)
all your examples compile, except the third, which explicitly requires the method to have two type variables.
The reason that your version doesn't work, is because Java's method references do not have one specific type. Instead, they have the type which is required in the given context. In your case, R
is inferred to be Long
because of the 4L
, but the getter cannot have the type Function<MyInterface,Long>
because in Java, generic types are invariant in their arguments.
Your code would compilewith( getNumber,"NO NUMBER")
which is not desired. Also it is not true that generics are always invariant (see stackoverflow.com/a/58378661/9549750 for a prove that generics of setters behave other then those of getters)
– jukzi
Oct 15 at 13:33
@jukzi Ah, my solution was already proposed by Avi. Too bad... :-). By the way, it is true that we can assign aThing<Cat>
to aThing<? extends Animal>
variable, but for real covariance I would expect that aThing<Cat>
can be assigned to aThing<Animal>
. Other languages, such as Kotlin, do allow to define co- and contravariant type variables.
– Hoopje
Oct 15 at 14:36
add a comment
|
Your Answer
StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");
StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "1"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);
else
createEditor();
);
function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/4.0/"u003ecc by-sa 4.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);
);
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f58378037%2fwhy-can-java-not-infer-a-supertype%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
6 Answers
6
active
oldest
votes
6 Answers
6
active
oldest
votes
active
oldest
votes
active
oldest
votes
This expression :
new Builder<MyInterface>().with(MyInterface::getNumber, 4L);
can be rewritten as :
new Builder<MyInterface>().with(myInterface -> myInterface.getNumber(), 4L);
Taking into account method signature :
public <F extends Function<T, R>, R> Builder<T> with(F getter, R returnValue)
R
will be inferred toLong
F
will beFunction<MyInterface, Long>
and you pass a method reference which will be infered as Function<MyInterface, Number>
This is the key - how should compiler predict that you actually want to return Long
from a function with such signature? It will not do the downcasting for you.
Since Number
is superclass of Long
and Number
is not necessairly a Long
(this is why it does not compile) - you would have to cast explicitly on your own :
new Builder<MyInterface>().with(myInterface -> (Long) myInterface.getNumber(), 4L);
making F
to be Function<MyIinterface, Long>
or pass generic arguments explicitly during method call as you did :
new Builder<MyInterface>().<Function<MyInterface, Number>, Number> with(MyInterface::getNumber, 4L);
and know R
will be seen as Number
and code will compile.
Thats an interesting typecast. But still i am searching for a definition of the method "with" which will make the caller compile without any cast. Anyway thanks for that idea.
– jukzi
Oct 14 at 14:12
@jukzi You can't. It does not matter how yourwith
is written. You haveMJyInterface::getNumber
has typeFunction<MyInterface, Number>
soR=Number
and then you also haveR=Long
from the other argument (remember that Java literals are not polymorphic!). At this point the compiler stops because it's not always possible to convert aNumber
to aLong
. The only way to fix this is to changeMyInterface
to use<A extends Number> Number
as return type, this makes the compiler haveR=A
and thenR=Long
and sinceA extends Number
it can substituteA=Long
– Giacomo Alzetta
Oct 15 at 9:27
add a comment
|
This expression :
new Builder<MyInterface>().with(MyInterface::getNumber, 4L);
can be rewritten as :
new Builder<MyInterface>().with(myInterface -> myInterface.getNumber(), 4L);
Taking into account method signature :
public <F extends Function<T, R>, R> Builder<T> with(F getter, R returnValue)
R
will be inferred toLong
F
will beFunction<MyInterface, Long>
and you pass a method reference which will be infered as Function<MyInterface, Number>
This is the key - how should compiler predict that you actually want to return Long
from a function with such signature? It will not do the downcasting for you.
Since Number
is superclass of Long
and Number
is not necessairly a Long
(this is why it does not compile) - you would have to cast explicitly on your own :
new Builder<MyInterface>().with(myInterface -> (Long) myInterface.getNumber(), 4L);
making F
to be Function<MyIinterface, Long>
or pass generic arguments explicitly during method call as you did :
new Builder<MyInterface>().<Function<MyInterface, Number>, Number> with(MyInterface::getNumber, 4L);
and know R
will be seen as Number
and code will compile.
Thats an interesting typecast. But still i am searching for a definition of the method "with" which will make the caller compile without any cast. Anyway thanks for that idea.
– jukzi
Oct 14 at 14:12
@jukzi You can't. It does not matter how yourwith
is written. You haveMJyInterface::getNumber
has typeFunction<MyInterface, Number>
soR=Number
and then you also haveR=Long
from the other argument (remember that Java literals are not polymorphic!). At this point the compiler stops because it's not always possible to convert aNumber
to aLong
. The only way to fix this is to changeMyInterface
to use<A extends Number> Number
as return type, this makes the compiler haveR=A
and thenR=Long
and sinceA extends Number
it can substituteA=Long
– Giacomo Alzetta
Oct 15 at 9:27
add a comment
|
This expression :
new Builder<MyInterface>().with(MyInterface::getNumber, 4L);
can be rewritten as :
new Builder<MyInterface>().with(myInterface -> myInterface.getNumber(), 4L);
Taking into account method signature :
public <F extends Function<T, R>, R> Builder<T> with(F getter, R returnValue)
R
will be inferred toLong
F
will beFunction<MyInterface, Long>
and you pass a method reference which will be infered as Function<MyInterface, Number>
This is the key - how should compiler predict that you actually want to return Long
from a function with such signature? It will not do the downcasting for you.
Since Number
is superclass of Long
and Number
is not necessairly a Long
(this is why it does not compile) - you would have to cast explicitly on your own :
new Builder<MyInterface>().with(myInterface -> (Long) myInterface.getNumber(), 4L);
making F
to be Function<MyIinterface, Long>
or pass generic arguments explicitly during method call as you did :
new Builder<MyInterface>().<Function<MyInterface, Number>, Number> with(MyInterface::getNumber, 4L);
and know R
will be seen as Number
and code will compile.
This expression :
new Builder<MyInterface>().with(MyInterface::getNumber, 4L);
can be rewritten as :
new Builder<MyInterface>().with(myInterface -> myInterface.getNumber(), 4L);
Taking into account method signature :
public <F extends Function<T, R>, R> Builder<T> with(F getter, R returnValue)
R
will be inferred toLong
F
will beFunction<MyInterface, Long>
and you pass a method reference which will be infered as Function<MyInterface, Number>
This is the key - how should compiler predict that you actually want to return Long
from a function with such signature? It will not do the downcasting for you.
Since Number
is superclass of Long
and Number
is not necessairly a Long
(this is why it does not compile) - you would have to cast explicitly on your own :
new Builder<MyInterface>().with(myInterface -> (Long) myInterface.getNumber(), 4L);
making F
to be Function<MyIinterface, Long>
or pass generic arguments explicitly during method call as you did :
new Builder<MyInterface>().<Function<MyInterface, Number>, Number> with(MyInterface::getNumber, 4L);
and know R
will be seen as Number
and code will compile.
edited Oct 14 at 14:49
answered Oct 14 at 14:03
michalkmichalk
6,0732 gold badges11 silver badges24 bronze badges
6,0732 gold badges11 silver badges24 bronze badges
Thats an interesting typecast. But still i am searching for a definition of the method "with" which will make the caller compile without any cast. Anyway thanks for that idea.
– jukzi
Oct 14 at 14:12
@jukzi You can't. It does not matter how yourwith
is written. You haveMJyInterface::getNumber
has typeFunction<MyInterface, Number>
soR=Number
and then you also haveR=Long
from the other argument (remember that Java literals are not polymorphic!). At this point the compiler stops because it's not always possible to convert aNumber
to aLong
. The only way to fix this is to changeMyInterface
to use<A extends Number> Number
as return type, this makes the compiler haveR=A
and thenR=Long
and sinceA extends Number
it can substituteA=Long
– Giacomo Alzetta
Oct 15 at 9:27
add a comment
|
Thats an interesting typecast. But still i am searching for a definition of the method "with" which will make the caller compile without any cast. Anyway thanks for that idea.
– jukzi
Oct 14 at 14:12
@jukzi You can't. It does not matter how yourwith
is written. You haveMJyInterface::getNumber
has typeFunction<MyInterface, Number>
soR=Number
and then you also haveR=Long
from the other argument (remember that Java literals are not polymorphic!). At this point the compiler stops because it's not always possible to convert aNumber
to aLong
. The only way to fix this is to changeMyInterface
to use<A extends Number> Number
as return type, this makes the compiler haveR=A
and thenR=Long
and sinceA extends Number
it can substituteA=Long
– Giacomo Alzetta
Oct 15 at 9:27
Thats an interesting typecast. But still i am searching for a definition of the method "with" which will make the caller compile without any cast. Anyway thanks for that idea.
– jukzi
Oct 14 at 14:12
Thats an interesting typecast. But still i am searching for a definition of the method "with" which will make the caller compile without any cast. Anyway thanks for that idea.
– jukzi
Oct 14 at 14:12
@jukzi You can't. It does not matter how your
with
is written. You have MJyInterface::getNumber
has type Function<MyInterface, Number>
so R=Number
and then you also have R=Long
from the other argument (remember that Java literals are not polymorphic!). At this point the compiler stops because it's not always possible to convert a Number
to a Long
. The only way to fix this is to change MyInterface
to use <A extends Number> Number
as return type, this makes the compiler have R=A
and then R=Long
and since A extends Number
it can substitute A=Long
– Giacomo Alzetta
Oct 15 at 9:27
@jukzi You can't. It does not matter how your
with
is written. You have MJyInterface::getNumber
has type Function<MyInterface, Number>
so R=Number
and then you also have R=Long
from the other argument (remember that Java literals are not polymorphic!). At this point the compiler stops because it's not always possible to convert a Number
to a Long
. The only way to fix this is to change MyInterface
to use <A extends Number> Number
as return type, this makes the compiler have R=A
and then R=Long
and since A extends Number
it can substitute A=Long
– Giacomo Alzetta
Oct 15 at 9:27
add a comment
|
The key to your error is in the generic declaration of the type of F
: F extends Function<T, R>
. The statement that does not work is: new Builder<MyInterface>().with(MyInterface::getNumber, 4L);
First, you have a new Builder<MyInterface>
. The declaration of the class therefore implies T = MyInterface
. As per your declaration of with
, F
must be a Function<T, R>
, which is a Function<MyInterface, R>
in this situation. Therefore, the parameter getter
must take a MyInterface
as parameter (satisfied by the method references MyInterface::getNumber
and MyInterface::getLong
), and return R
, which must be the same type as the second parameter to the function with
. Now, let's see if this holds for all of your cases:
// T = MyInterface, F = Function<MyInterface, Long>, R = Long
new Builder<MyInterface>().with(MyInterface::getLong, 4L);
// T = MyInterface, F = Function<MyInterface, Number>, R = Number
// 4L explicitly widened to Number
new Builder<MyInterface>().with(MyInterface::getNumber, (Number) 4L);
// T = MyInterface, F = Function<MyInterface, Number>, R = Number
// 4L implicitly widened to Number
new Builder<MyInterface>().<Function<MyInterface, Number>, Number>with(MyInterface::getNumber, 4L);
// T = MyInterface, F = Function<MyInterface, Number>, R = Number
// 4L implicitly widened to Number
new Builder<MyInterface>().with((Function<MyInterface, Number>) MyInterface::getNumber, 4L);
// T = MyInterface, F = Function<MyInterface, Number>, R = Long
// F = Function<T, not R> violates definition, therefore compilation error occurs
// Compiler cannot infer type of method reference and 4L at the same time,
// so it keeps the type of 4L as Long and attempts to infer a match for MyInterface::getNumber,
// only to find that the types don't match up
new Builder<MyInterface>().with(MyInterface::getNumber, 4L);
You can "fix" this problem with the following options:
// stick to Long
new Builder<MyInterface>().with(MyInterface::getLong, 4L);
// stick to Number
new Builder<MyInterface>().with(MyInterface::getNumber, (Number) 4L);
// explicitly convert the result of getNumber:
new Builder<MyInterface>().with(myInstance -> (Long) myInstance.getNumber(), 4L);
// explicitly convert the result of getLong:
new Builder<MyInterface>().with(myInterface -> (Number) myInterface.getLong(), (Number) 4L);
Beyond this point, it's mostly a design decision for which option reduces code complexity for your particular application, so choose whatever fits you best.
The reason that you cannot do this without casting lies in the following, from the Java Language Specification:
Boxing conversion treats expressions of a primitive type as expressions of a corresponding reference type. Specifically, the following nine conversions are called the boxing conversions:
- From type boolean to type Boolean
- From type byte to type Byte
- From type short to type Short
- From type char to type Character
- From type int to type Integer
- From type long to type Long
- From type float to type Float
- From type double to type Double
- From the null type to the null type
As you can clearly see, there is no implicit boxing conversion from long to Number, and the widening conversion from Long to Number can only occur when the compiler is sure that it requires a Number and not a Long. As there is a conflict between the method reference that requires a Number and the 4L that provides a Long, the compiler (for some reason???) unable to make the logical leap that Long is-a Number and deduce that F
is a Function<MyInterface, Number>
.
Instead, I managed to resolve the problem by slightly editing the function signature:
public <R> Builder<T> with(Function<T, ? super R> getter, R returnValue)
return null;//TODO
After this change, the following occurs:
// doesn't work, as it should not work
new Builder<MyInterface>().with(MyInterface::getLong, (Number), 4L);
// works, as it always did
new Builder<MyInterface>().with(MyInterface::getLong, 4L);
// works, as it should work
new Builder<MyInterface>().with(MyInterface::getNumber, (Number)4L);
// works, as you wanted
new Builder<MyInterface>().with(MyInterface::getNumber, 4L);
Edit:
After spending some more time on it, it's annoyingly difficult to enforce getter-based type safety. Here's a working example that uses setter methods to enforce a builder's type-safety:
public class Builder<T>
static public interface MyInterface
//setters
void number(Number number);
void Long(Long Long);
void string(String string);
//getters
Number number();
Long Long();
String string();
// whatever object we're building, let's say it's just a MyInterface for now...
private T buildee = (T) new MyInterface()
private String string;
private Long Long;
private Number number;
public void number(Number number)
this.number = number;
public void Long(Long Long)
this.Long = Long;
public void string(String string)
this.string = string;
public Number number()
return this.number;
public Long Long()
return this.Long;
public String string()
return this.string;
;
public <R> Builder<T> with(BiConsumer<T, R> setter, R val)
setter.accept(this.buildee, val); // take the buildee, and set the appropriate value
return this;
public static void main(String[] args)
// works:
new Builder<MyInterface>().with(MyInterface::Long, 4L);
// works:
new Builder<MyInterface>().with(MyInterface::number, (Number) 4L);
// compile time error, as it shouldn't work
new Builder<MyInterface>().with(MyInterface::Long, (Number) 4L);
// works, as it always did
new Builder<MyInterface>().with(MyInterface::Long, 4L);
// works, as it should
new Builder<MyInterface>().with(MyInterface::number, (Number)4L);
// works, as you wanted
new Builder<MyInterface>().with(MyInterface::number, 4L);
// compile time error, as you wanted
new Builder<MyInterface>().with(MyInterface::number, "blah");
Provided the type-safe ability to construct an object, hopefully at some point in the future we'll be able to return an immutable data object from the builder (maybe by adding a toRecord()
method to the interface, and specifying the builder as a Builder<IntermediaryInterfaceType, RecordType>
), so you don't even have to worry about the resulting object being modified. Honestly, it's an absolute shame that it requires so much effort to get a type-safe field-flexible builder, but it's probably impossible without some new features, code generation, or an annoying amount of reflection.
Thanks for all your work but i do not see any improvement towards avoiding the manual typecast. My naive understanding is that a compiler should be able to infer (i.e. typecast) everything that a human can.
– jukzi
Oct 14 at 14:24
@jukzi My naive understanding is the same, but for whatever reason it doesn't work that way. I've come up with a workaround that pretty much achieves the desired effect, though
– Avi
Oct 14 at 14:39
Thanks again. But your new proposal is too wide (see stackoverflow.com/questions/58337639) since it allows compilation of ".with(MyInterface::getNumber, "I AM NOT A NUMBER")";
– jukzi
Oct 14 at 14:42
your sentence "Compiler cannot infer type of method reference and 4L at the same time" is cool. But i just want it the other way arround. the compiler should try Number based on the first parameter and do a widening to Number of the second parameter.
– jukzi
Oct 14 at 14:45
1
WHAAAAAAAAAAAT? Why does BiConsumer work as intended while Function does not? I do not get the idea. I admit this is exactly the typesafety i wanted but unfortunatlly does not work with getters. WHY WHY WHY.
– jukzi
Oct 14 at 16:22
|
show 5 more comments
The key to your error is in the generic declaration of the type of F
: F extends Function<T, R>
. The statement that does not work is: new Builder<MyInterface>().with(MyInterface::getNumber, 4L);
First, you have a new Builder<MyInterface>
. The declaration of the class therefore implies T = MyInterface
. As per your declaration of with
, F
must be a Function<T, R>
, which is a Function<MyInterface, R>
in this situation. Therefore, the parameter getter
must take a MyInterface
as parameter (satisfied by the method references MyInterface::getNumber
and MyInterface::getLong
), and return R
, which must be the same type as the second parameter to the function with
. Now, let's see if this holds for all of your cases:
// T = MyInterface, F = Function<MyInterface, Long>, R = Long
new Builder<MyInterface>().with(MyInterface::getLong, 4L);
// T = MyInterface, F = Function<MyInterface, Number>, R = Number
// 4L explicitly widened to Number
new Builder<MyInterface>().with(MyInterface::getNumber, (Number) 4L);
// T = MyInterface, F = Function<MyInterface, Number>, R = Number
// 4L implicitly widened to Number
new Builder<MyInterface>().<Function<MyInterface, Number>, Number>with(MyInterface::getNumber, 4L);
// T = MyInterface, F = Function<MyInterface, Number>, R = Number
// 4L implicitly widened to Number
new Builder<MyInterface>().with((Function<MyInterface, Number>) MyInterface::getNumber, 4L);
// T = MyInterface, F = Function<MyInterface, Number>, R = Long
// F = Function<T, not R> violates definition, therefore compilation error occurs
// Compiler cannot infer type of method reference and 4L at the same time,
// so it keeps the type of 4L as Long and attempts to infer a match for MyInterface::getNumber,
// only to find that the types don't match up
new Builder<MyInterface>().with(MyInterface::getNumber, 4L);
You can "fix" this problem with the following options:
// stick to Long
new Builder<MyInterface>().with(MyInterface::getLong, 4L);
// stick to Number
new Builder<MyInterface>().with(MyInterface::getNumber, (Number) 4L);
// explicitly convert the result of getNumber:
new Builder<MyInterface>().with(myInstance -> (Long) myInstance.getNumber(), 4L);
// explicitly convert the result of getLong:
new Builder<MyInterface>().with(myInterface -> (Number) myInterface.getLong(), (Number) 4L);
Beyond this point, it's mostly a design decision for which option reduces code complexity for your particular application, so choose whatever fits you best.
The reason that you cannot do this without casting lies in the following, from the Java Language Specification:
Boxing conversion treats expressions of a primitive type as expressions of a corresponding reference type. Specifically, the following nine conversions are called the boxing conversions:
- From type boolean to type Boolean
- From type byte to type Byte
- From type short to type Short
- From type char to type Character
- From type int to type Integer
- From type long to type Long
- From type float to type Float
- From type double to type Double
- From the null type to the null type
As you can clearly see, there is no implicit boxing conversion from long to Number, and the widening conversion from Long to Number can only occur when the compiler is sure that it requires a Number and not a Long. As there is a conflict between the method reference that requires a Number and the 4L that provides a Long, the compiler (for some reason???) unable to make the logical leap that Long is-a Number and deduce that F
is a Function<MyInterface, Number>
.
Instead, I managed to resolve the problem by slightly editing the function signature:
public <R> Builder<T> with(Function<T, ? super R> getter, R returnValue)
return null;//TODO
After this change, the following occurs:
// doesn't work, as it should not work
new Builder<MyInterface>().with(MyInterface::getLong, (Number), 4L);
// works, as it always did
new Builder<MyInterface>().with(MyInterface::getLong, 4L);
// works, as it should work
new Builder<MyInterface>().with(MyInterface::getNumber, (Number)4L);
// works, as you wanted
new Builder<MyInterface>().with(MyInterface::getNumber, 4L);
Edit:
After spending some more time on it, it's annoyingly difficult to enforce getter-based type safety. Here's a working example that uses setter methods to enforce a builder's type-safety:
public class Builder<T>
static public interface MyInterface
//setters
void number(Number number);
void Long(Long Long);
void string(String string);
//getters
Number number();
Long Long();
String string();
// whatever object we're building, let's say it's just a MyInterface for now...
private T buildee = (T) new MyInterface()
private String string;
private Long Long;
private Number number;
public void number(Number number)
this.number = number;
public void Long(Long Long)
this.Long = Long;
public void string(String string)
this.string = string;
public Number number()
return this.number;
public Long Long()
return this.Long;
public String string()
return this.string;
;
public <R> Builder<T> with(BiConsumer<T, R> setter, R val)
setter.accept(this.buildee, val); // take the buildee, and set the appropriate value
return this;
public static void main(String[] args)
// works:
new Builder<MyInterface>().with(MyInterface::Long, 4L);
// works:
new Builder<MyInterface>().with(MyInterface::number, (Number) 4L);
// compile time error, as it shouldn't work
new Builder<MyInterface>().with(MyInterface::Long, (Number) 4L);
// works, as it always did
new Builder<MyInterface>().with(MyInterface::Long, 4L);
// works, as it should
new Builder<MyInterface>().with(MyInterface::number, (Number)4L);
// works, as you wanted
new Builder<MyInterface>().with(MyInterface::number, 4L);
// compile time error, as you wanted
new Builder<MyInterface>().with(MyInterface::number, "blah");
Provided the type-safe ability to construct an object, hopefully at some point in the future we'll be able to return an immutable data object from the builder (maybe by adding a toRecord()
method to the interface, and specifying the builder as a Builder<IntermediaryInterfaceType, RecordType>
), so you don't even have to worry about the resulting object being modified. Honestly, it's an absolute shame that it requires so much effort to get a type-safe field-flexible builder, but it's probably impossible without some new features, code generation, or an annoying amount of reflection.
Thanks for all your work but i do not see any improvement towards avoiding the manual typecast. My naive understanding is that a compiler should be able to infer (i.e. typecast) everything that a human can.
– jukzi
Oct 14 at 14:24
@jukzi My naive understanding is the same, but for whatever reason it doesn't work that way. I've come up with a workaround that pretty much achieves the desired effect, though
– Avi
Oct 14 at 14:39
Thanks again. But your new proposal is too wide (see stackoverflow.com/questions/58337639) since it allows compilation of ".with(MyInterface::getNumber, "I AM NOT A NUMBER")";
– jukzi
Oct 14 at 14:42
your sentence "Compiler cannot infer type of method reference and 4L at the same time" is cool. But i just want it the other way arround. the compiler should try Number based on the first parameter and do a widening to Number of the second parameter.
– jukzi
Oct 14 at 14:45
1
WHAAAAAAAAAAAT? Why does BiConsumer work as intended while Function does not? I do not get the idea. I admit this is exactly the typesafety i wanted but unfortunatlly does not work with getters. WHY WHY WHY.
– jukzi
Oct 14 at 16:22
|
show 5 more comments
The key to your error is in the generic declaration of the type of F
: F extends Function<T, R>
. The statement that does not work is: new Builder<MyInterface>().with(MyInterface::getNumber, 4L);
First, you have a new Builder<MyInterface>
. The declaration of the class therefore implies T = MyInterface
. As per your declaration of with
, F
must be a Function<T, R>
, which is a Function<MyInterface, R>
in this situation. Therefore, the parameter getter
must take a MyInterface
as parameter (satisfied by the method references MyInterface::getNumber
and MyInterface::getLong
), and return R
, which must be the same type as the second parameter to the function with
. Now, let's see if this holds for all of your cases:
// T = MyInterface, F = Function<MyInterface, Long>, R = Long
new Builder<MyInterface>().with(MyInterface::getLong, 4L);
// T = MyInterface, F = Function<MyInterface, Number>, R = Number
// 4L explicitly widened to Number
new Builder<MyInterface>().with(MyInterface::getNumber, (Number) 4L);
// T = MyInterface, F = Function<MyInterface, Number>, R = Number
// 4L implicitly widened to Number
new Builder<MyInterface>().<Function<MyInterface, Number>, Number>with(MyInterface::getNumber, 4L);
// T = MyInterface, F = Function<MyInterface, Number>, R = Number
// 4L implicitly widened to Number
new Builder<MyInterface>().with((Function<MyInterface, Number>) MyInterface::getNumber, 4L);
// T = MyInterface, F = Function<MyInterface, Number>, R = Long
// F = Function<T, not R> violates definition, therefore compilation error occurs
// Compiler cannot infer type of method reference and 4L at the same time,
// so it keeps the type of 4L as Long and attempts to infer a match for MyInterface::getNumber,
// only to find that the types don't match up
new Builder<MyInterface>().with(MyInterface::getNumber, 4L);
You can "fix" this problem with the following options:
// stick to Long
new Builder<MyInterface>().with(MyInterface::getLong, 4L);
// stick to Number
new Builder<MyInterface>().with(MyInterface::getNumber, (Number) 4L);
// explicitly convert the result of getNumber:
new Builder<MyInterface>().with(myInstance -> (Long) myInstance.getNumber(), 4L);
// explicitly convert the result of getLong:
new Builder<MyInterface>().with(myInterface -> (Number) myInterface.getLong(), (Number) 4L);
Beyond this point, it's mostly a design decision for which option reduces code complexity for your particular application, so choose whatever fits you best.
The reason that you cannot do this without casting lies in the following, from the Java Language Specification:
Boxing conversion treats expressions of a primitive type as expressions of a corresponding reference type. Specifically, the following nine conversions are called the boxing conversions:
- From type boolean to type Boolean
- From type byte to type Byte
- From type short to type Short
- From type char to type Character
- From type int to type Integer
- From type long to type Long
- From type float to type Float
- From type double to type Double
- From the null type to the null type
As you can clearly see, there is no implicit boxing conversion from long to Number, and the widening conversion from Long to Number can only occur when the compiler is sure that it requires a Number and not a Long. As there is a conflict between the method reference that requires a Number and the 4L that provides a Long, the compiler (for some reason???) unable to make the logical leap that Long is-a Number and deduce that F
is a Function<MyInterface, Number>
.
Instead, I managed to resolve the problem by slightly editing the function signature:
public <R> Builder<T> with(Function<T, ? super R> getter, R returnValue)
return null;//TODO
After this change, the following occurs:
// doesn't work, as it should not work
new Builder<MyInterface>().with(MyInterface::getLong, (Number), 4L);
// works, as it always did
new Builder<MyInterface>().with(MyInterface::getLong, 4L);
// works, as it should work
new Builder<MyInterface>().with(MyInterface::getNumber, (Number)4L);
// works, as you wanted
new Builder<MyInterface>().with(MyInterface::getNumber, 4L);
Edit:
After spending some more time on it, it's annoyingly difficult to enforce getter-based type safety. Here's a working example that uses setter methods to enforce a builder's type-safety:
public class Builder<T>
static public interface MyInterface
//setters
void number(Number number);
void Long(Long Long);
void string(String string);
//getters
Number number();
Long Long();
String string();
// whatever object we're building, let's say it's just a MyInterface for now...
private T buildee = (T) new MyInterface()
private String string;
private Long Long;
private Number number;
public void number(Number number)
this.number = number;
public void Long(Long Long)
this.Long = Long;
public void string(String string)
this.string = string;
public Number number()
return this.number;
public Long Long()
return this.Long;
public String string()
return this.string;
;
public <R> Builder<T> with(BiConsumer<T, R> setter, R val)
setter.accept(this.buildee, val); // take the buildee, and set the appropriate value
return this;
public static void main(String[] args)
// works:
new Builder<MyInterface>().with(MyInterface::Long, 4L);
// works:
new Builder<MyInterface>().with(MyInterface::number, (Number) 4L);
// compile time error, as it shouldn't work
new Builder<MyInterface>().with(MyInterface::Long, (Number) 4L);
// works, as it always did
new Builder<MyInterface>().with(MyInterface::Long, 4L);
// works, as it should
new Builder<MyInterface>().with(MyInterface::number, (Number)4L);
// works, as you wanted
new Builder<MyInterface>().with(MyInterface::number, 4L);
// compile time error, as you wanted
new Builder<MyInterface>().with(MyInterface::number, "blah");
Provided the type-safe ability to construct an object, hopefully at some point in the future we'll be able to return an immutable data object from the builder (maybe by adding a toRecord()
method to the interface, and specifying the builder as a Builder<IntermediaryInterfaceType, RecordType>
), so you don't even have to worry about the resulting object being modified. Honestly, it's an absolute shame that it requires so much effort to get a type-safe field-flexible builder, but it's probably impossible without some new features, code generation, or an annoying amount of reflection.
The key to your error is in the generic declaration of the type of F
: F extends Function<T, R>
. The statement that does not work is: new Builder<MyInterface>().with(MyInterface::getNumber, 4L);
First, you have a new Builder<MyInterface>
. The declaration of the class therefore implies T = MyInterface
. As per your declaration of with
, F
must be a Function<T, R>
, which is a Function<MyInterface, R>
in this situation. Therefore, the parameter getter
must take a MyInterface
as parameter (satisfied by the method references MyInterface::getNumber
and MyInterface::getLong
), and return R
, which must be the same type as the second parameter to the function with
. Now, let's see if this holds for all of your cases:
// T = MyInterface, F = Function<MyInterface, Long>, R = Long
new Builder<MyInterface>().with(MyInterface::getLong, 4L);
// T = MyInterface, F = Function<MyInterface, Number>, R = Number
// 4L explicitly widened to Number
new Builder<MyInterface>().with(MyInterface::getNumber, (Number) 4L);
// T = MyInterface, F = Function<MyInterface, Number>, R = Number
// 4L implicitly widened to Number
new Builder<MyInterface>().<Function<MyInterface, Number>, Number>with(MyInterface::getNumber, 4L);
// T = MyInterface, F = Function<MyInterface, Number>, R = Number
// 4L implicitly widened to Number
new Builder<MyInterface>().with((Function<MyInterface, Number>) MyInterface::getNumber, 4L);
// T = MyInterface, F = Function<MyInterface, Number>, R = Long
// F = Function<T, not R> violates definition, therefore compilation error occurs
// Compiler cannot infer type of method reference and 4L at the same time,
// so it keeps the type of 4L as Long and attempts to infer a match for MyInterface::getNumber,
// only to find that the types don't match up
new Builder<MyInterface>().with(MyInterface::getNumber, 4L);
You can "fix" this problem with the following options:
// stick to Long
new Builder<MyInterface>().with(MyInterface::getLong, 4L);
// stick to Number
new Builder<MyInterface>().with(MyInterface::getNumber, (Number) 4L);
// explicitly convert the result of getNumber:
new Builder<MyInterface>().with(myInstance -> (Long) myInstance.getNumber(), 4L);
// explicitly convert the result of getLong:
new Builder<MyInterface>().with(myInterface -> (Number) myInterface.getLong(), (Number) 4L);
Beyond this point, it's mostly a design decision for which option reduces code complexity for your particular application, so choose whatever fits you best.
The reason that you cannot do this without casting lies in the following, from the Java Language Specification:
Boxing conversion treats expressions of a primitive type as expressions of a corresponding reference type. Specifically, the following nine conversions are called the boxing conversions:
- From type boolean to type Boolean
- From type byte to type Byte
- From type short to type Short
- From type char to type Character
- From type int to type Integer
- From type long to type Long
- From type float to type Float
- From type double to type Double
- From the null type to the null type
As you can clearly see, there is no implicit boxing conversion from long to Number, and the widening conversion from Long to Number can only occur when the compiler is sure that it requires a Number and not a Long. As there is a conflict between the method reference that requires a Number and the 4L that provides a Long, the compiler (for some reason???) unable to make the logical leap that Long is-a Number and deduce that F
is a Function<MyInterface, Number>
.
Instead, I managed to resolve the problem by slightly editing the function signature:
public <R> Builder<T> with(Function<T, ? super R> getter, R returnValue)
return null;//TODO
After this change, the following occurs:
// doesn't work, as it should not work
new Builder<MyInterface>().with(MyInterface::getLong, (Number), 4L);
// works, as it always did
new Builder<MyInterface>().with(MyInterface::getLong, 4L);
// works, as it should work
new Builder<MyInterface>().with(MyInterface::getNumber, (Number)4L);
// works, as you wanted
new Builder<MyInterface>().with(MyInterface::getNumber, 4L);
Edit:
After spending some more time on it, it's annoyingly difficult to enforce getter-based type safety. Here's a working example that uses setter methods to enforce a builder's type-safety:
public class Builder<T>
static public interface MyInterface
//setters
void number(Number number);
void Long(Long Long);
void string(String string);
//getters
Number number();
Long Long();
String string();
// whatever object we're building, let's say it's just a MyInterface for now...
private T buildee = (T) new MyInterface()
private String string;
private Long Long;
private Number number;
public void number(Number number)
this.number = number;
public void Long(Long Long)
this.Long = Long;
public void string(String string)
this.string = string;
public Number number()
return this.number;
public Long Long()
return this.Long;
public String string()
return this.string;
;
public <R> Builder<T> with(BiConsumer<T, R> setter, R val)
setter.accept(this.buildee, val); // take the buildee, and set the appropriate value
return this;
public static void main(String[] args)
// works:
new Builder<MyInterface>().with(MyInterface::Long, 4L);
// works:
new Builder<MyInterface>().with(MyInterface::number, (Number) 4L);
// compile time error, as it shouldn't work
new Builder<MyInterface>().with(MyInterface::Long, (Number) 4L);
// works, as it always did
new Builder<MyInterface>().with(MyInterface::Long, 4L);
// works, as it should
new Builder<MyInterface>().with(MyInterface::number, (Number)4L);
// works, as you wanted
new Builder<MyInterface>().with(MyInterface::number, 4L);
// compile time error, as you wanted
new Builder<MyInterface>().with(MyInterface::number, "blah");
Provided the type-safe ability to construct an object, hopefully at some point in the future we'll be able to return an immutable data object from the builder (maybe by adding a toRecord()
method to the interface, and specifying the builder as a Builder<IntermediaryInterfaceType, RecordType>
), so you don't even have to worry about the resulting object being modified. Honestly, it's an absolute shame that it requires so much effort to get a type-safe field-flexible builder, but it's probably impossible without some new features, code generation, or an annoying amount of reflection.
edited Oct 14 at 16:31
answered Oct 14 at 14:15
AviAvi
2,1441 gold badge6 silver badges19 bronze badges
2,1441 gold badge6 silver badges19 bronze badges
Thanks for all your work but i do not see any improvement towards avoiding the manual typecast. My naive understanding is that a compiler should be able to infer (i.e. typecast) everything that a human can.
– jukzi
Oct 14 at 14:24
@jukzi My naive understanding is the same, but for whatever reason it doesn't work that way. I've come up with a workaround that pretty much achieves the desired effect, though
– Avi
Oct 14 at 14:39
Thanks again. But your new proposal is too wide (see stackoverflow.com/questions/58337639) since it allows compilation of ".with(MyInterface::getNumber, "I AM NOT A NUMBER")";
– jukzi
Oct 14 at 14:42
your sentence "Compiler cannot infer type of method reference and 4L at the same time" is cool. But i just want it the other way arround. the compiler should try Number based on the first parameter and do a widening to Number of the second parameter.
– jukzi
Oct 14 at 14:45
1
WHAAAAAAAAAAAT? Why does BiConsumer work as intended while Function does not? I do not get the idea. I admit this is exactly the typesafety i wanted but unfortunatlly does not work with getters. WHY WHY WHY.
– jukzi
Oct 14 at 16:22
|
show 5 more comments
Thanks for all your work but i do not see any improvement towards avoiding the manual typecast. My naive understanding is that a compiler should be able to infer (i.e. typecast) everything that a human can.
– jukzi
Oct 14 at 14:24
@jukzi My naive understanding is the same, but for whatever reason it doesn't work that way. I've come up with a workaround that pretty much achieves the desired effect, though
– Avi
Oct 14 at 14:39
Thanks again. But your new proposal is too wide (see stackoverflow.com/questions/58337639) since it allows compilation of ".with(MyInterface::getNumber, "I AM NOT A NUMBER")";
– jukzi
Oct 14 at 14:42
your sentence "Compiler cannot infer type of method reference and 4L at the same time" is cool. But i just want it the other way arround. the compiler should try Number based on the first parameter and do a widening to Number of the second parameter.
– jukzi
Oct 14 at 14:45
1
WHAAAAAAAAAAAT? Why does BiConsumer work as intended while Function does not? I do not get the idea. I admit this is exactly the typesafety i wanted but unfortunatlly does not work with getters. WHY WHY WHY.
– jukzi
Oct 14 at 16:22
Thanks for all your work but i do not see any improvement towards avoiding the manual typecast. My naive understanding is that a compiler should be able to infer (i.e. typecast) everything that a human can.
– jukzi
Oct 14 at 14:24
Thanks for all your work but i do not see any improvement towards avoiding the manual typecast. My naive understanding is that a compiler should be able to infer (i.e. typecast) everything that a human can.
– jukzi
Oct 14 at 14:24
@jukzi My naive understanding is the same, but for whatever reason it doesn't work that way. I've come up with a workaround that pretty much achieves the desired effect, though
– Avi
Oct 14 at 14:39
@jukzi My naive understanding is the same, but for whatever reason it doesn't work that way. I've come up with a workaround that pretty much achieves the desired effect, though
– Avi
Oct 14 at 14:39
Thanks again. But your new proposal is too wide (see stackoverflow.com/questions/58337639) since it allows compilation of ".with(MyInterface::getNumber, "I AM NOT A NUMBER")";
– jukzi
Oct 14 at 14:42
Thanks again. But your new proposal is too wide (see stackoverflow.com/questions/58337639) since it allows compilation of ".with(MyInterface::getNumber, "I AM NOT A NUMBER")";
– jukzi
Oct 14 at 14:42
your sentence "Compiler cannot infer type of method reference and 4L at the same time" is cool. But i just want it the other way arround. the compiler should try Number based on the first parameter and do a widening to Number of the second parameter.
– jukzi
Oct 14 at 14:45
your sentence "Compiler cannot infer type of method reference and 4L at the same time" is cool. But i just want it the other way arround. the compiler should try Number based on the first parameter and do a widening to Number of the second parameter.
– jukzi
Oct 14 at 14:45
1
1
WHAAAAAAAAAAAT? Why does BiConsumer work as intended while Function does not? I do not get the idea. I admit this is exactly the typesafety i wanted but unfortunatlly does not work with getters. WHY WHY WHY.
– jukzi
Oct 14 at 16:22
WHAAAAAAAAAAAT? Why does BiConsumer work as intended while Function does not? I do not get the idea. I admit this is exactly the typesafety i wanted but unfortunatlly does not work with getters. WHY WHY WHY.
– jukzi
Oct 14 at 16:22
|
show 5 more comments
It seems the compiler has used the value 4L to decide that R is Long, and getNumber() returns a Number, which is not necessarily a Long.
But I'm not sure why the value takes precedence over the method...
add a comment
|
It seems the compiler has used the value 4L to decide that R is Long, and getNumber() returns a Number, which is not necessarily a Long.
But I'm not sure why the value takes precedence over the method...
add a comment
|
It seems the compiler has used the value 4L to decide that R is Long, and getNumber() returns a Number, which is not necessarily a Long.
But I'm not sure why the value takes precedence over the method...
It seems the compiler has used the value 4L to decide that R is Long, and getNumber() returns a Number, which is not necessarily a Long.
But I'm not sure why the value takes precedence over the method...
answered Oct 14 at 13:53
RickRick
26611 bronze badges
26611 bronze badges
add a comment
|
add a comment
|
The Java compiler is in general not good at inferring multiple/nested generic types or wildcards. Often I cannot get something to compile without using a helper function to capture or infer some of the types.
But, do you really need to capture the exact type of Function
as F
? If not, maybe the following works, and as you can see, also seems to work with subtypes of Function
.
import java.util.function.Function;
import java.util.function.UnaryOperator;
public class Builder<T>
public interface MyInterface
Number getNumber();
Long getLong();
public <R> Builder<T> with(Function<T, R> getter, R returnValue)
return null;
// example subclass of Function
private static UnaryOperator<String> stringFunc = (s) -> (s + ".");
public static void main(String[] args)
// works
new Builder<MyInterface>().with(MyInterface::getNumber, 4L);
// works
new Builder<String>().with(stringFunc, "s");
"with(MyInterface::getNumber, "NOT A NUMBER")" should not compile
– jukzi
Oct 15 at 8:20
add a comment
|
The Java compiler is in general not good at inferring multiple/nested generic types or wildcards. Often I cannot get something to compile without using a helper function to capture or infer some of the types.
But, do you really need to capture the exact type of Function
as F
? If not, maybe the following works, and as you can see, also seems to work with subtypes of Function
.
import java.util.function.Function;
import java.util.function.UnaryOperator;
public class Builder<T>
public interface MyInterface
Number getNumber();
Long getLong();
public <R> Builder<T> with(Function<T, R> getter, R returnValue)
return null;
// example subclass of Function
private static UnaryOperator<String> stringFunc = (s) -> (s + ".");
public static void main(String[] args)
// works
new Builder<MyInterface>().with(MyInterface::getNumber, 4L);
// works
new Builder<String>().with(stringFunc, "s");
"with(MyInterface::getNumber, "NOT A NUMBER")" should not compile
– jukzi
Oct 15 at 8:20
add a comment
|
The Java compiler is in general not good at inferring multiple/nested generic types or wildcards. Often I cannot get something to compile without using a helper function to capture or infer some of the types.
But, do you really need to capture the exact type of Function
as F
? If not, maybe the following works, and as you can see, also seems to work with subtypes of Function
.
import java.util.function.Function;
import java.util.function.UnaryOperator;
public class Builder<T>
public interface MyInterface
Number getNumber();
Long getLong();
public <R> Builder<T> with(Function<T, R> getter, R returnValue)
return null;
// example subclass of Function
private static UnaryOperator<String> stringFunc = (s) -> (s + ".");
public static void main(String[] args)
// works
new Builder<MyInterface>().with(MyInterface::getNumber, 4L);
// works
new Builder<String>().with(stringFunc, "s");
The Java compiler is in general not good at inferring multiple/nested generic types or wildcards. Often I cannot get something to compile without using a helper function to capture or infer some of the types.
But, do you really need to capture the exact type of Function
as F
? If not, maybe the following works, and as you can see, also seems to work with subtypes of Function
.
import java.util.function.Function;
import java.util.function.UnaryOperator;
public class Builder<T>
public interface MyInterface
Number getNumber();
Long getLong();
public <R> Builder<T> with(Function<T, R> getter, R returnValue)
return null;
// example subclass of Function
private static UnaryOperator<String> stringFunc = (s) -> (s + ".");
public static void main(String[] args)
// works
new Builder<MyInterface>().with(MyInterface::getNumber, 4L);
// works
new Builder<String>().with(stringFunc, "s");
answered Oct 15 at 0:58
machfourmachfour
611 bronze badge
611 bronze badge
"with(MyInterface::getNumber, "NOT A NUMBER")" should not compile
– jukzi
Oct 15 at 8:20
add a comment
|
"with(MyInterface::getNumber, "NOT A NUMBER")" should not compile
– jukzi
Oct 15 at 8:20
"with(MyInterface::getNumber, "NOT A NUMBER")" should not compile
– jukzi
Oct 15 at 8:20
"with(MyInterface::getNumber, "NOT A NUMBER")" should not compile
– jukzi
Oct 15 at 8:20
add a comment
|
The most interesting part lies in the difference between those 2 lines, I think:
// works:
new Builder<MyInterface>().<Function<MyInterface, Number>, Number> with(MyInterface::getNumber, 4L);
// compilation error: Cannot infer ...
new Builder<MyInterface>().with(MyInterface::getNumber, 4L);
In the first case, the T
is explicitly Number
, so 4L
is also a Number
, no problem. In the second case, 4L
is a Long
, so T
is a Long
, so your function is not compatible, and Java cannot know if you meant Number
or Long
.
add a comment
|
The most interesting part lies in the difference between those 2 lines, I think:
// works:
new Builder<MyInterface>().<Function<MyInterface, Number>, Number> with(MyInterface::getNumber, 4L);
// compilation error: Cannot infer ...
new Builder<MyInterface>().with(MyInterface::getNumber, 4L);
In the first case, the T
is explicitly Number
, so 4L
is also a Number
, no problem. In the second case, 4L
is a Long
, so T
is a Long
, so your function is not compatible, and Java cannot know if you meant Number
or Long
.
add a comment
|
The most interesting part lies in the difference between those 2 lines, I think:
// works:
new Builder<MyInterface>().<Function<MyInterface, Number>, Number> with(MyInterface::getNumber, 4L);
// compilation error: Cannot infer ...
new Builder<MyInterface>().with(MyInterface::getNumber, 4L);
In the first case, the T
is explicitly Number
, so 4L
is also a Number
, no problem. In the second case, 4L
is a Long
, so T
is a Long
, so your function is not compatible, and Java cannot know if you meant Number
or Long
.
The most interesting part lies in the difference between those 2 lines, I think:
// works:
new Builder<MyInterface>().<Function<MyInterface, Number>, Number> with(MyInterface::getNumber, 4L);
// compilation error: Cannot infer ...
new Builder<MyInterface>().with(MyInterface::getNumber, 4L);
In the first case, the T
is explicitly Number
, so 4L
is also a Number
, no problem. In the second case, 4L
is a Long
, so T
is a Long
, so your function is not compatible, and Java cannot know if you meant Number
or Long
.
answered Oct 15 at 1:39
njzk2njzk2
34.1k5 gold badges54 silver badges97 bronze badges
34.1k5 gold badges54 silver badges97 bronze badges
add a comment
|
add a comment
|
With the following signature:
public <R> Test<T> with(Function<T, ? super R> getter, R returnValue)
all your examples compile, except the third, which explicitly requires the method to have two type variables.
The reason that your version doesn't work, is because Java's method references do not have one specific type. Instead, they have the type which is required in the given context. In your case, R
is inferred to be Long
because of the 4L
, but the getter cannot have the type Function<MyInterface,Long>
because in Java, generic types are invariant in their arguments.
Your code would compilewith( getNumber,"NO NUMBER")
which is not desired. Also it is not true that generics are always invariant (see stackoverflow.com/a/58378661/9549750 for a prove that generics of setters behave other then those of getters)
– jukzi
Oct 15 at 13:33
@jukzi Ah, my solution was already proposed by Avi. Too bad... :-). By the way, it is true that we can assign aThing<Cat>
to aThing<? extends Animal>
variable, but for real covariance I would expect that aThing<Cat>
can be assigned to aThing<Animal>
. Other languages, such as Kotlin, do allow to define co- and contravariant type variables.
– Hoopje
Oct 15 at 14:36
add a comment
|
With the following signature:
public <R> Test<T> with(Function<T, ? super R> getter, R returnValue)
all your examples compile, except the third, which explicitly requires the method to have two type variables.
The reason that your version doesn't work, is because Java's method references do not have one specific type. Instead, they have the type which is required in the given context. In your case, R
is inferred to be Long
because of the 4L
, but the getter cannot have the type Function<MyInterface,Long>
because in Java, generic types are invariant in their arguments.
Your code would compilewith( getNumber,"NO NUMBER")
which is not desired. Also it is not true that generics are always invariant (see stackoverflow.com/a/58378661/9549750 for a prove that generics of setters behave other then those of getters)
– jukzi
Oct 15 at 13:33
@jukzi Ah, my solution was already proposed by Avi. Too bad... :-). By the way, it is true that we can assign aThing<Cat>
to aThing<? extends Animal>
variable, but for real covariance I would expect that aThing<Cat>
can be assigned to aThing<Animal>
. Other languages, such as Kotlin, do allow to define co- and contravariant type variables.
– Hoopje
Oct 15 at 14:36
add a comment
|
With the following signature:
public <R> Test<T> with(Function<T, ? super R> getter, R returnValue)
all your examples compile, except the third, which explicitly requires the method to have two type variables.
The reason that your version doesn't work, is because Java's method references do not have one specific type. Instead, they have the type which is required in the given context. In your case, R
is inferred to be Long
because of the 4L
, but the getter cannot have the type Function<MyInterface,Long>
because in Java, generic types are invariant in their arguments.
With the following signature:
public <R> Test<T> with(Function<T, ? super R> getter, R returnValue)
all your examples compile, except the third, which explicitly requires the method to have two type variables.
The reason that your version doesn't work, is because Java's method references do not have one specific type. Instead, they have the type which is required in the given context. In your case, R
is inferred to be Long
because of the 4L
, but the getter cannot have the type Function<MyInterface,Long>
because in Java, generic types are invariant in their arguments.
answered Oct 15 at 12:48
HoopjeHoopje
10.6k6 gold badges28 silver badges44 bronze badges
10.6k6 gold badges28 silver badges44 bronze badges
Your code would compilewith( getNumber,"NO NUMBER")
which is not desired. Also it is not true that generics are always invariant (see stackoverflow.com/a/58378661/9549750 for a prove that generics of setters behave other then those of getters)
– jukzi
Oct 15 at 13:33
@jukzi Ah, my solution was already proposed by Avi. Too bad... :-). By the way, it is true that we can assign aThing<Cat>
to aThing<? extends Animal>
variable, but for real covariance I would expect that aThing<Cat>
can be assigned to aThing<Animal>
. Other languages, such as Kotlin, do allow to define co- and contravariant type variables.
– Hoopje
Oct 15 at 14:36
add a comment
|
Your code would compilewith( getNumber,"NO NUMBER")
which is not desired. Also it is not true that generics are always invariant (see stackoverflow.com/a/58378661/9549750 for a prove that generics of setters behave other then those of getters)
– jukzi
Oct 15 at 13:33
@jukzi Ah, my solution was already proposed by Avi. Too bad... :-). By the way, it is true that we can assign aThing<Cat>
to aThing<? extends Animal>
variable, but for real covariance I would expect that aThing<Cat>
can be assigned to aThing<Animal>
. Other languages, such as Kotlin, do allow to define co- and contravariant type variables.
– Hoopje
Oct 15 at 14:36
Your code would compile
with( getNumber,"NO NUMBER")
which is not desired. Also it is not true that generics are always invariant (see stackoverflow.com/a/58378661/9549750 for a prove that generics of setters behave other then those of getters)– jukzi
Oct 15 at 13:33
Your code would compile
with( getNumber,"NO NUMBER")
which is not desired. Also it is not true that generics are always invariant (see stackoverflow.com/a/58378661/9549750 for a prove that generics of setters behave other then those of getters)– jukzi
Oct 15 at 13:33
@jukzi Ah, my solution was already proposed by Avi. Too bad... :-). By the way, it is true that we can assign a
Thing<Cat>
to a Thing<? extends Animal>
variable, but for real covariance I would expect that a Thing<Cat>
can be assigned to a Thing<Animal>
. Other languages, such as Kotlin, do allow to define co- and contravariant type variables.– Hoopje
Oct 15 at 14:36
@jukzi Ah, my solution was already proposed by Avi. Too bad... :-). By the way, it is true that we can assign a
Thing<Cat>
to a Thing<? extends Animal>
variable, but for real covariance I would expect that a Thing<Cat>
can be assigned to a Thing<Animal>
. Other languages, such as Kotlin, do allow to define co- and contravariant type variables.– Hoopje
Oct 15 at 14:36
add a comment
|
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f58378037%2fwhy-can-java-not-infer-a-supertype%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Can you post
MyInterface
?– Maurice Perry
Oct 14 at 13:42
its already inside the class
– jukzi
Oct 14 at 13:42
Hmm I tried
<F extends Function<T, R>, R, S extends R> Builder<T> with(F getter, S returnValue)
but gotjava.lang.Number cannot be converted to java.lang.Long)
, which is surprising because I don't see where the compiler gets the idea that the return value fromgetter
will need to be converted toreturnValue
.– jingx
Oct 14 at 14:56
@jukzi OK. Sorry, I missed that.
– Maurice Perry
Oct 15 at 4:58
Changing
Number getNumber()
to<A extends Number> A getNumber()
makes stuff work. No idea whether this is what you wanted. As others have said the issue is thatMyInterface::getNumber
could be a function that returnsDouble
for example and notLong
. Your declaration does not allow the compiler to narrow the return type based on other information present. By using a generic return type you allow the compiler to do so, hence it works.– Giacomo Alzetta
Oct 15 at 9:19