java - Why can this generic method with a bound return any type? -


why following code compile? method ielement.getx(string) returns instance of type ielement or of subclasses thereof. code in main class invokes getx(string) method. compiler allows store return value variable of type integer (which not in hierarchy of ielement).

public interface ielement extends charsequence {   <t extends ielement> t getx(string value); }  public class main {   public void example(ielement element) {     integer x = element.getx("x");   } } 

shouldn't return type still instance of ielement - after type erasure?

the bytecode of getx(string) method is:

public abstract <t extends ielement> t getx(java.lang.string); flags: acc_public, acc_abstract signature: #7                           // <t::lielement;>(ljava/lang/string;)tt; 

edit: replaced string consistently integer.

this legitimate type inference*.

we can reduce following example (ideone):

interface foo {     <f extends foo> f bar();      public static void main(string[] args) {         foo foo = null;         string baz = foo.bar();     } } 

the compiler allowed infer (nonsensical, really) intersection type string & foo because foo interface. example in question, integer & ielement inferred.

it's nonsensical because conversion impossible. can't such cast ourselves:

// won't compile because integer final integer x = (integer & ielement) element; 

type inference works with:

  • a set of inference variables each of method's type parameters.
  • a set of bounds must conformed to.
  • sometimes constraints, reduced bounds.

at end of algorithm, each variable resolved intersection type based on bound set, , if they're valid, invocation compiles.

the process begins in 8.1.3:

when inference begins, bound set typically generated list of type parameter declarations p1, ..., pp , associated inference variables α1, ..., αp. such bound set constructed follows. each l (1 ≤ l ≤ p):

  • […]

  • otherwise, each type t delimited & in typebound, bound αl <: t[p1:=α1, ..., pp:=αp] appears in set […].

so, means first compiler starts bound of f <: foo (which means f subtype of foo).

moving 18.5.2, return target type gets considered:

if invocation poly expression, […] let r return type of m, let t invocation's target type, , then:

  • […]

  • otherwise, constraint formula ‹r θ → t› reduced , incorporated [the bound set].

the constraint formula ‹r θ → t› gets reduced bound of r θ <: t, have f <: string.

later on these resolved according 18.4:

[…] candidate instantiation ti defined each αi:

  • otherwise, αi has proper upper bounds u1, ..., uk, ti = glb(u1, ..., uk).

the bounds α1 = t1, ..., αn = tn incorporated current bound set.

recall our set of bounds f <: foo, f <: string. glb(string, foo) defined string & foo. apparently legitimate type glb, requires that:

it compile-time error if, 2 classes (not interfaces) vi , vj, vi not subclass of vj or vice versa.

finally:

if resolution succeeds instantiations t1, ..., tp inference variables α1, ..., αp, let θ' substitution [p1:=t1, ..., pp:=tp]. then:

  • if unchecked conversion not necessary method applicable, invocation type of m obtained applying θ' type of m.

the method therefore invoked string & foo type of f. can of course assign string, impossibly converting foo string.

the fact string/integer final classes apparently not considered.


* note: type erasure is/was unrelated issue.

also, while compiles on java 7 well, think it's reasonable needn't worry specification there. java 7's type inference less sophisticated version of java 8's. compiles similar reasons.


as addendum, while strange, never cause problem not present. it's useful write generic method return type solely inferred return target, because null can returned such method without casting.

suppose example have map analog stores subtypes of particular interface:

interface fooimplmap {     void put(string key, foo value);     <f extends foo> f get(string key); }  class bar implements foo {} class biz implements foo {} 

it's valid make error such following:

fooimplmap m = ...; m.put("b", new bar()); biz b = m.get("b"); // casting bar biz 

so fact can also integer = m.get("b"); not new possibility error. if programming code this, potentially unsound begin with.

generally, type parameter should solely inferred target type if there no reason bound it, e.g. collections.emptylist() , optional.empty():

private static final optional<?> empty = new optional<>();  public static<t> optional<t> empty() {     @suppresswarnings("unchecked")     optional<t> t = (optional<t>) empty;     return t; } 

this a-ok because optional.empty() can neither produce nor consume t.


Comments

Popular posts from this blog

powershell Start-Process exit code -1073741502 when used with Credential from a windows service environment -

twig - Using Twigbridge in a Laravel 5.1 Package -

c# - LINQ join Entities from HashSet's, Join vs Dictionary vs HashSet performance -