java - Why can't a generic interface be used as a type parameter? -
here java generic code programming language pragmatics, scott
interface chooser<t> { public boolean better(t a, t b); } class arbiter<t> { t bestsofar; chooser<? super t> comp; public arbiter(chooser<? super t> c) { comp = c; } public void consider(t t) { if (bestsofar == null || comp.better(t, bestsofar)) bestsofar = t; } public t best() { return bestsofar; } } class casesensitive implements chooser<string> { public boolean better(string a, string b) { return a.compareto(b) < 1; } } ... arbiter<string> csnames = new arbiter<string>(new casesensitive()); csnames.consider(new string("apple")); csnames.consider(new string("aardvark")); system.out.println(csnames.best()); // prints "apple"
java requires code each generic class manifestly (self-obviously) type safe, independent of particular instantiation. means type of field
comp
—and in particular, fact providesbetter
method—must statically declared. result,chooser
used givenarbiter
instance must specified constructor parameter; cannot generic parameter. (we have used constructor parameter in c++; in java mandatory.)
- what "code each generic class manifestly (self-obviously) type safe, independent of particular instantiation" mean?
- why lead conclusion "the type of field
comp
—and in particular, fact providesbetter
method—must statically declared"? - what "statically declared" mean? opposed to?
what when
chooser
used generic parameter in java (even though not correct)? want see looks wrong in java. "we have used constructor parameter in c++; in java mandatory" mean wrong way written in c++ way (i saw similar code in c ++ earlier in book, quoted in previous post)? following wrong code in java, implied "chooser
cannot generic parameter":class arbiter<t, chooser> { t bestsofar; chooser comp; public arbiter(chooser c) { comp = c; } public void consider(t t) { if (bestsofar == null || comp.better(t, bestsofar)) bestsofar = t; } public t best() { return bestsofar; } }
thanks.
quick disclaimer: don't know c++ anywhere near know java. if did wrong in answer i'm happy correct it.
most of questions here can answered explaining important difference between java generics , c++ templates. java generics implemented type erasure means compiler transforms generic code in non-generic code removing references generics , inserting casts appropriate. example, might have generic code follows:
class holder<t> { private t obj; holder(t obj) { this.obj = obj; } t get() { return obj; } } holder<string> h = new holder<string>("hello"); string s = h.get();
and during compilation that's transformed in code more this:
class holder { private object obj; holder(object obj) { this.obj = obj; } object get() { return obj; } } holder h = new holder("hello"); string s = (string) h.get();
by contrast, c++ templates more sophisticated copy , paste. might start code this:
template<typename t> class holder { t obj; public: holder(t obj) : obj(obj) {} t& get() { return obj; } }; holder<std::string> h{"hello"}; std::string& s = h.get();
and when holder<std::string>
, compiler creates new class (called template instantiation) has t
replaced std::string
:
class holder_std_string { std::string obj; public: holder_std_string(std::string obj) : obj(obj) {} std::string& get() { return obj; } };
the major implications of are:
- each template instantiation "knows" type argument inside class body.
- the compiler can generate different code different template instantiations.
for example, might observe in following method of c++ arbiter
:
void consider(t* t) { if (!best_so_far || comp(*t, *best_so_far)) best_so_far = t; // ^^^^^^^^^^^^^^^^^^^^^^ note! }
the method body expects template argument c
has operator overload ()
, there's nothing template declaration indicates this. instead, c++ has duck typing via templates. template instantiation arbiter<string, case_sensitive>
knows template argument case_sensitive
, can therefore generate code calls overloaded ()
operator. can pass template argument long has overloaded ()
operator compatible expression calls it. if passed argument c
didn't have overloaded ()
operator, template instantiation fail compile.
another interesting note c++ code didn't need pass case_sensitive
object constructor because template instantiation knows how default construct 1 itself.
for question #4, making chooser
type generic parameter in java this:
class arbiter<t, c extends chooser<? super t>> { t bestsofar; c comp; public arbiter(c c) { comp = c; } public void consider(t t) { if (bestsofar == null || comp.better(t, bestsofar)) bestsofar = t; } public t best() { return bestsofar; } }
however, note we've accomplished via bounded type variable c extends chooser<? super t>
tells compiler c
must subtype of chooser
. how compiler knows it's possible call method comp.better(...)
, method expression refers to. remember there's ever 1 arbiter
class in java generic arbiters
need share bytecode.
the c++ code has no such requirement because validity of expression comp(...)
decided during instantiation of template.
what "statically declared" mean? opposed to?
in context means it's known @ compile-time. illustrate:
// know statically (at compile-time) // n type if number. void m(number n) { // determine @ runtime // actual type of n couple // of different ways. if (n instanceof double) /* n double */; // prints actual type of n. system.out.println(n.getclass()); }
types in c++ statically-known too. it's c++ templates implemented in way allows many things java generics don't allow.
wiki
Comments
Post a Comment