Created
December 2, 2011 20:48
-
-
Save rose00/1424776 to your computer and use it in GitHub Desktop.
demonstration of API incompatibility between wildcards vs. named type parameters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Test program to show that replacing wildcards by named type parameters, | |
* or vice versa, is not a safe change to a public API. | |
* If such a change were compatible, then the overrides below would succeed (sans "QQ"). | |
* | |
* In fact, JLS 8.4.2 (Method Signature) defines override equivalence as | |
* allowing renaming of type parameters, but not substituting type parameters | |
* for wildcards. Thus, the two kinds of type quantification are not equivalent. | |
* Think of it this way: A type parameter can be freely renamed, but only if it | |
* already has a name. | |
* | |
* JLS 8.4.8.3 (Requirements in Overriding and Hiding) defines the common-sense | |
* requirement that "near misses" are usually illegal: If two methods fail to | |
* be override-equivalent, then they must have distinct erasures, if they are | |
* to be present in the same class or interface. | |
* | |
* For non-overridable program elements such as constructors, the logical | |
* requirements are not so strict, but the fact remains that the 'Signature' | |
* attributes change (non-trivially) when wildcards are interchanged with | |
* named type parameters. Therefore, it is best to avoid such changes | |
* for any public API element. | |
* | |
* If the (private) body of the API element requires names for the | |
* wildcard types, two options are open to clean up the code. | |
* First, continue to use wildcards in the body, and use casts | |
* with @SuppressWarnings("unchecked") to force the various names | |
* to appear type-compatible. | |
* Second, refactor the body of the API element into a private | |
* subroutine, with explicit type parameters. | |
* | |
* (Corrections welcome.) | |
*/ | |
// To compile: $JAVA8_HOME/bin/javac -Xlint:all APICheck.java | |
// ASM disassembly shows the 'Signature' attributes for the test methods. | |
// Remove "QQ" below to elicit the given errors. | |
import java.util.Map; | |
class APICheck { | |
interface Super1 { | |
void test1(Map<? extends Number, ?> map); | |
// signature (Ljava/util/Map<+Ljava/lang/Number;*>;)V | |
// declaration: void test1(java.util.Map<? extends java.lang.Number, ?>) | |
} | |
interface Sub1 extends Super1 { | |
// error: name clash: <K,V>test1(Map<K,V>) in Sub1 | |
// and test1(Map<? extends Number,?>) in Super1 | |
// have the same erasure, yet neither overrides the other | |
<K extends Number, V> | |
void QQtest1(Map<K, V> map); | |
} | |
interface Super2 { | |
<K extends Number, V> | |
void test2(Map<K, V> map); | |
// signature <K:Ljava/lang/Number;V:Ljava/lang/Object;>(Ljava/util/Map<TK;TV;>;)V | |
// declaration: void test2<K extends java.lang.Number, V>(java.util.Map<K, V>) | |
} | |
interface Sub2 extends Super2 { | |
// error: name clash: test2(Map<?,? extends Number>) in Sub2 | |
// and <K,V>test2(Map<K,V>) in Super2 | |
// have the same erasure, yet neither overrides the other | |
void QQtest2(Map<?, ? extends Number> map); | |
} | |
} | |
/* | |
// class version 51.0 (51) | |
// access flags 0x600 | |
abstract interface APICheck$Super1 { | |
// access flags 0x608 | |
static abstract INNERCLASS APICheck$Super1 APICheck Super1 | |
// access flags 0x401 | |
// signature (Ljava/util/Map<+Ljava/lang/Number;*>;)V | |
// declaration: void test1(java.util.Map<? extends java.lang.Number, ?>) | |
public abstract test1(Ljava/util/Map;)V | |
} | |
// class version 51.0 (51) | |
// access flags 0x600 | |
abstract interface APICheck$Sub1 implements APICheck$Super1 { | |
// access flags 0x608 | |
static abstract INNERCLASS APICheck$Sub1 APICheck Sub1 | |
// access flags 0x608 | |
static abstract INNERCLASS APICheck$Super1 APICheck Super1 | |
// access flags 0x401 | |
// signature <K:Ljava/lang/Number;V:Ljava/lang/Object;>(Ljava/util/Map<TK;TV;>;)V | |
// declaration: void QQtest1<K extends java.lang.Number, V>(java.util.Map<K, V>) | |
public abstract QQtest1(Ljava/util/Map;)V | |
} | |
// class version 51.0 (51) | |
// access flags 0x600 | |
abstract interface APICheck$Super2 { | |
// access flags 0x608 | |
static abstract INNERCLASS APICheck$Super2 APICheck Super2 | |
// access flags 0x401 | |
// signature <K:Ljava/lang/Number;V:Ljava/lang/Object;>(Ljava/util/Map<TK;TV;>;)V | |
// declaration: void test2<K extends java.lang.Number, V>(java.util.Map<K, V>) | |
public abstract test2(Ljava/util/Map;)V | |
} | |
// class version 51.0 (51) | |
// access flags 0x600 | |
abstract interface APICheck$Sub2 implements APICheck$Super2 { | |
// access flags 0x608 | |
static abstract INNERCLASS APICheck$Sub2 APICheck Sub2 | |
// access flags 0x608 | |
static abstract INNERCLASS APICheck$Super2 APICheck Super2 | |
// access flags 0x401 | |
// signature (Ljava/util/Map<*+Ljava/lang/Number;>;)V | |
// declaration: void QQtest2(java.util.Map<?, ? extends java.lang.Number>) | |
public abstract QQtest2(Ljava/util/Map;)V | |
} | |
// class version 51.0 (51) | |
// access flags 0x20 | |
class APICheck { | |
// access flags 0x608 | |
static abstract INNERCLASS APICheck$Sub2 APICheck Sub2 | |
// access flags 0x608 | |
static abstract INNERCLASS APICheck$Super2 APICheck Super2 | |
// access flags 0x608 | |
static abstract INNERCLASS APICheck$Sub1 APICheck Sub1 | |
// access flags 0x608 | |
static abstract INNERCLASS APICheck$Super1 APICheck Super1 | |
// access flags 0x0 | |
<init>()V | |
ALOAD 0 | |
INVOKESPECIAL java/lang/Object.<init> ()V | |
RETURN | |
MAXSTACK = 1 | |
MAXLOCALS = 1 | |
} | |
*/ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment