In this Java class hierarchy, how to use Generics so that type casts are unnecessary? -
suppose have 2 classes called realnumber
, intnumber
, , method called plus
:
public class realnumber<s extends realnumber> { public s plus(s realnumber) { ... } } public class intnumber extends realnumber<intnumber> { }
when using plus
method compiler warnings , errors:
realnumber x = new realnumber(); intnumber y = new intnumber(); realnumber sum1 = x.plus(x); // warning: unchecked call 'plus(s)' member of raw type 'realnumber'. realnumber sum2 = x.plus(y); // warning: unchecked call 'plus(s)' member of raw type 'realnumber'. realnumber sum3 = y.plus(x); // error: plus (intnumber) in realnumber cannot applied (realnumber). realnumber sum4 = y.plus(y); // works fine. intnumber sum5 = y.plus(x); // error: plus (intnumber) in realnumber cannot applied (realnumber). intnumber sum6 = y.plus(y); // works fine.
to fix this, modify plus
method, follows:
public <t extends realnumber> s plus(t realnumber) { ... }
and works fine. good! however, want add third class called positiveintnumber
extends intnumber
:
public class positiveintnumber extends intnumber { }
this, of course, not work:
realnumber x = new realnumber(); intnumber y = new intnumber(); positiveintnumber z = new positiveintnumber(); realnumber sum1 = x.plus(x); // fine. realnumber sum2 = x.plus(y); // fine. realnumber sum3 = y.plus(x); // fine. realnumber sum4 = y.plus(y); // fine. intnumber sum5 = y.plus(x); // fine. intnumber sum6 = y.plus(y); // fine. realnumber sum7 = x.plus(z); // fine. intnumber sum8 = y.plus(z); // fine. positiveintnumber sum9 = z.plus(x); // error: incompatible types: no instance(s) of type variable(s) t exist intnumber conforms positiveintnumber positiveintnumber sum10 = z.plus(y); // error: incompatible types: no instance(s) of type variable(s) t exist intnumber conforms positiveintnumber positiveintnumber sum11 = z.plus(z); // error: incompatible types: no instance(s) of type variable(s) t exist intnumber conforms positiveintnumber
to fix again, modify class definitions, follows:
public class intnumber<s extends intnumber> extends realnumber<s> { } public class positiveintnumber extends intnumber<positiveintnumber> { }
this solves problem positiveintnumber
, breaks intnumber
:
realnumber x = new realnumber(); intnumber y = new intnumber(); positiveintnumber z = new positiveintnumber(); realnumber sum1 = x.plus(x); // fine. realnumber sum2 = x.plus(y); // fine. realnumber sum3 = y.plus(x); // fine. realnumber sum4 = y.plus(y); // fine. intnumber sum5 = y.plus(x); // error: incompatible types: realnumber cannot converted intnumber. intnumber sum6 = y.plus(y); // error: incompatible types: realnumber cannot converted intnumber. realnumber sum7 = x.plus(z); // fine. intnumber sum8 = y.plus(z); // error: incompatible types: realnumber cannot converted intnumber. positiveintnumber sum9 = z.plus(x); // fine. positiveintnumber sum10 = z.plus(y); // fine. positiveintnumber sum11 = z.plus(z); // fine.
a cast fixes it, should unnecessary, in view:
intnumber sum5 = (intnumber)y.plus(x);
so, have 2 questions:
1) since y
intnumber
, , return type s
extends intnumber
, why y.plus(...) return realnumber
?
2) how fix this?
edit: writing realnumber<realnumber>
should unnecessary, since realnumber<intnumber>
, realnumber<positiveintnumber>
make absolutely no sense. maybe entire use of generics plain wrong. think answers question 1: y.plus(...)
returns realnumber
because y raw, java type system doesn't care anymore s
extends intnumber
. however, want avoid repeating plus
method in subclasses of realnumber, , should able use generics, in way, avoid that. question 2 still stands. should doing?
note: actual classes not reals , integers, other complicated business classes. think of them classes a, b, c , don't question model. "addables", yes, here x.plus(y) should return type x, every x , y.
i'm assuming trying create type hierarchy expresses "addables" return, given 2 "operands", more general of two, using inheritance model mathematical relationship of "special case".
before go further, should consider this link uses square is-a rectangle example illustrate why use of inheritance bad idea, in case of immutable objects ones you're proposing might ok.
you want both int.plus( real ) , real.plus( int ) return real, , int.plus( int ) return int. same pattern should work nat.plus( int ), etc.
as long types statically known, should piece of cake type-wise.
class real { real plus( real r ) { ... } int floor() { ... } } class int extends real { int plus( int ) { return plus( i.asreal() ).floor(); } nat abs() { ... } real asreal() { return this; } } class nat extends int { nat plus( nat n ) { return plus( n.asint() ).abs(); } int asint() { return this; } }
overloading plus
allows compiler determine "most specific" plus operation can statically verified. compiler can't know adding 2 reals happen integers results in real that's integer. matter, non-integers add integers; i'm sure don't intend type system represent fact somehow.
under arrangement, sum5
, sum9
, , sum10
type check, (to mind) should be.
incidentally, respect "curiously recurring type constraint" pattern trying demonstrate, you're doing wrong. need supply type parameter everywhere generic type occurs, inside type bound itself:
class g< t extends g< t > > {}
you, , other commenters , responders here, have been leaving off second t
.
Comments
Post a Comment