How can parameterized methods be implicitly bound in some cases, and not with others?

I recently ran into this problem while updating refactoring code:

The getList () method below has a parameterized return type. Below I have set three methods that try to implicitly bind <T> to <Integer> .

I cannot understand why the first two are compiled and executed correctly, while the third (bindViaMethodInvocation) does not even compile.

Any clues?

Looking for a similar question in StackOverflow, I came across this question: Alleged wildcards in the inverse type . The answer there ( Laurence Gonsalves credit) contains a couple of useful links to explain what should happen: "The problem here (as you suggested) is that the compiler is executing Capture Conversion . I believe this is a result of Β§15.12.2.6 JLS JLS. "

 package stackoverflow; import java.util.*; public class ParameterizedReturn { // Parameterized method public static <T extends Object> List<T> getList() { return new ArrayList<T>(); } public static List<Integer> bindViaReturnStatement() { return getList(); } public static List<Integer> bindViaVariableAssignment() { List<Integer> intList = getList(); return intList; } public static List<Integer> bindViaMethodInvocation() { // Compile error here return echo(getList()); } public static List<Integer> echo(List<Integer> intList) { return intList; } } 
+4
source share
2 answers

The first two methods use getList() in the context of the assignment conversion, either the List<Integer> assignment or the return statement from the method that returns List<Integer> . The same is not true for bindViaMethodInvocation - using an expression as a method argument is not subject to assignment conversion.

From the JLS section 15.12.2.8 :

If any of the arguments of the method type has not been inferred from the types of the actual arguments, they are now inferred as follows.

  • If the result of a method occurs in a context in which it will undergo an assignment transformation (Section 5.2) to type S, then let R be the declared type of the result of the method, and let R '= R [T1 = B (T1) ... Tn = B (Tn)], where B (Ti) is the type deduced for Ti in the previous section, or Ti if no type was accepted.

JLS is not entirely clear why return statements are referenced here. The closest I can find is at 14.17 :

A return statement with an expression must be contained in a method declaration declared to return a value (Β§8.4) or a compile-time error occurs. The expression must denote a variable or value of some type T, or a compile-time error occurs. Type T must be assigned (Β§5.2) to the declared method result type or a compile-time error has occurred.

(It would be nice if section 5.2 states that return statements can be converted to assignments.)

+7
source

JLS 3 # 12.15.2.8 allows type inference in restricted contexts. I rate it as a design error. The meaning of the expression should be free of context, which would be easier for everyone.

Since the value of getList() varies depending on its environment, which contradicts the intuition of Java programmers (never before), you find it a mystery that the first 2 compilations, and the third not. And you are not alone, this question has been raised repeatedly. They can tell us RTFS, but the more you need to read the specification, the worse the specification will be.

Of course, we must be practical if the context-sensitive interpretation is really useful and necessary. However, little is confirmed. This type of withdrawal is dangerously dangerous, and most of its used 99% is erroneously designed. It is unclear what they meant by what they consider necessary to add an inference rule of this type.

If the Java generics are "reified", that is, the value of T available for calling the method at runtime, we can imagine that such type inference would be safe and useful. However, T not available at run time, so calling the getList() context is free, it is not possible to return the correct type expected by the calling site. If there is no extralinguistic application logic that protects the sound type. Then this is hardly "static typing."

Some people went further and requested the following type of output:

 Object getFoo(){ .. } Bar bar = getFoo(); 

because "if I wrote this, I certainly know that the return type at runtime is Bar, so stop asking me, silly compiler!"

I respect this opinion, but you should choose the language of difference. In both static and dynamic typing, the programmer knows the types, but the point of static input is that we want to explicitly write the types in the source anyway - not to help the compiler, but to benefit. The "type of withdrawal" violated this exact purpose; this should be done only if he does not create any myth to anyone who reads the code of what actually exists. Unfortunately, Java type inference is very mythical.

0
source

Source: https://habr.com/ru/post/1346751/


All Articles