Title: C625 Project
1C625 Project Covariant Return Types
Group 5 members Irene Cheng Nicholas Lamb Wesley
Mackay December 4th, 2002
2- Overview
- Motivation
- Project Objective
- Design Implementation
- Test example
- Conclusions Future Work
3- Motivation
- Return type of an overriding method must
match return type of the top-level declaration - This restriction simplifies the language but
makes it inflexible - Type information can be lost
- Casting can regain type information, but makes
code less readable, slower and error-prone
4Current Java Return Mechanism
Vector
Object elementAt(int)Object
firstElement()Object lastElement()
Invariant return type
PointVector
ObjectObjectObject
elementAt(int)firstElement()lastElement()
5Better Return Mechanism
Vector
Object elementAt(int)Object
firstElement()Object lastElement()
Covariant return type
PointVector
PointPointPoint
elementAt(int)firstElement()lastElement()
6- Coding Example
- In current Java, we must use PointVector as
follows - Point origin(0,0)PointVector shapePts new
PointVector() - shapePts.addElement( new Point(1,1)
)shapePts.addElement( new Point(2,-5)
)shapePts.addElement( new Point(-3,0) ) - // 2nd argument requires an explicit // cast,
which is costly and confusing - distanceBetween(origin, (Point)
shapePts.firstElement() )
7- Coding Example
- With covariant return types, we could use
PointVector as follows - Point origin(0,0)PointVector shapePts new
PointVector() - shapePts.addElement( new Point(1,1)
)shapePts.addElement( new Point(2,-5)
)shapePts.addElement( new Point(-3,0) ) - // 2nd argument does not require casting,//
making code simpler and safer - distanceBetween(origin, (Point)
shapePts.firstElement() )
8Project Objective
- Add support for covariant return types to
improve flexibility and maintainability - No changes to the JVM compiler
- No impact on current invariant return type
mechanism (backwards compatibility)
9Design Implementation
- Phase 1
- Add support for covariant return types to virtual
methods defined in class hierarchies - Phase 2
- Apply concept of covariance to primitive return
types - Phase 3
- Extend the above capabilities to interface methods
10Phase 1 - Type Checking Mechanism
ClassD public ClassA foo()
ClassA
ClassE public ClassB foo()
ClassB
ClassC
What return types should be allowed?
ClassF public ??? foo()
11Design option 1 Check against top level
ClassD public ClassA foo()
ClassA
ClassE public ClassB foo()
ClassB
ClassC
Covariant only against the top-level declaration
ClassF public ClassC foo()
12Problem Context Dependent
ClassD public ClassA foo()
ClassA
ClassE public ClassB foo()
ClassB
ClassC
Covariant rules broken by the removal of ClassD
ClassF public ClassC foo()
13Design option 2 Check against next level
ClassD public ClassA foo()
ClassA
ClassE public ClassB foo()
ClassB
ClassC
Covariant model always maintained at all levels
ClassF public ClassB foo()
14Design option 2 Check against next level
ClassD public ClassA foo()
ClassA
ClassE public ClassB foo()
ClassB
ClassC
Covariant model preserved when ClassD is removed
ClassF public ClassB foo()
15Phase 2 - Primitive Types Hierarchy
- Concept of covariant return types can be
applied to Java primitives because of widening
conversions - Widening conversions offer a form of
substitutability - Consider that an integer value is also a
floating point value - We can safely return an int at any call site
expecting a float without affecting program logic
16Phase 2 - Primitive Types Hierarchy
double
float
boolean
long
int
short
char
byte
17Handling Arrays
- Array return types require extra checking
- We verify that the arrays element type is
covariant and that the dimensions match - Suppose in the parent we have a method
- int omega(int i, float f)
- Then in a subclass
- int omega(int i, float f)
- double omega(int i, float f)
- short omega(int i, float f)
???
18Phase 3 - Covariant Returns for Interfaces
- The implementation of an interface method can
have a return type that is covariant to what is
declared in the interface - Since a class can implement multiple
interfaces, the return type of an interface
method should be covariant to all interface
methods with the same signature
19Phase 3 - Example of interfaces
InterfaceA public short foo()
InterfaceB public float foo()
ClassC implements InterfaceA, InterfaceB public
??? foo()
- We could not define such a ClassC in
conventional Java because it would be impossible
to satisfy both interfaces
20Phase 3 - Example of interfaces
InterfaceA public short foo()
InterfaceB public float foo()
ClassC implements InterfaceA, InterfaceB public
short foo()
- With our extension, we could return a short or
a byte because either of these is covariant to
both short and float
21Phase 3 - Example of interfaces
InterfaceA public boolean foo()
InterfaceB public float foo()
ClassC implements InterfaceA, InterfaceB public
??? foo()
- Implementing multiple interfaces can still fail
if theres no common covariant type
22Test Example
ClassX
ClassA
interfaceI
ClassB
ClassC
ClassY
interfaceJ
ClassD
ClassE
23Test Example
class ClassX ClassA classTest(Object o)
ClassA obj new ClassA(0) return obj
interface interfaceI public int alpha(int
a, int b) interface interfaceJ public
ClassC theta(float e)
24Test Example
class ClassY extends ClassX implements
interfaceI, interfaceJ ClassD
classTest(Object o) ClassD obj new
ClassD(0) return obj public double
alpha(int a, int b) return 0 public
ClassB theta(float e) return new
ClassB(12)
25Sample Output - Invalid return types
JVM Warning Messages Warning In method
ClassAalpha(II)D D is not an assignment subtype
of I Warning In method ClassYtheta(F)LClassB
LClassB is not an assignment subtype of LClassC
26Conclusions Future Work
- Covariant return types increase language
flexibility and reduces the need for casting - Offers backwards compatibility
- The JVMs treatment of signature introduces
problems when dispatching - Need to remove the return type from the
signature string whenever two signatures are
compared - Better to store the return type in a separate
string, apart from the method arguments