Skip to content

Instantly share code, notes, and snippets.

@rose00
Created December 2, 2011 20:48
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rose00/1424776 to your computer and use it in GitHub Desktop.
Save rose00/1424776 to your computer and use it in GitHub Desktop.
demonstration of API incompatibility between wildcards vs. named type parameters
/*
* 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