Is there any way using Java generics to express a type hierarchy like the following?
class A { ... }
class B extends A { ... }
interface Wraps<T> {
T getWrapped();
}
class WrapsA implements Wraps<A> {
A getWrapped();
}
class WrapsB extends WrapsA /* implements Wraps<B> */ {
B getWrapped();
}
I understand that as written Java's type system will reject this code, but is there any way I can specify the type parameters or inheritance hierarchy that will get this working?
EDIT: I realized that the real issue I'm having is I'm trying to enforce type safety in my API for a method like
<T extends A> T unwrap(Wraps<T> wrapper, Class<T> wrappedClass);
I'm not able to call it with unwrap(wrapsB, B.class). Is this something that Java will handle?
You can just use
class WrapsB extends WrapsA {
B getWrapped();
}
Since you're allowed to return subclasses in overriden methods (it's possible from Java SE6, as far as I remember)
Check out self-bounded types.
interface Wraps<T, W extends Wraps<T, W>> { ... }
abstract class AbstractWrapsA< T extends A, W extends AbstractWrapsA< T, W > implements Wraps< T, W > { ... }
class WrapsA extends AbstractWrapsA< A, WrapsA > {}
class WrapsB extends AbstractWrapsA< B, WrapsB > { ... }
EDIT: Actually to satisfy the OP's need we can dispense with self-bounded types and just migrate the guts of WrapsA to an abstract class. We still do not have that WrapsB both is-a WrapsA and is-a Wraps<B>.
Related
For example,
public interface Foo<T extends Blackness, S extends T & Whiteness> { }
Error: Type parameter cannot be followed by other bounds
T extends Blackness and S extends T so S inherently extends Blackness. The only other contingency is that S must also extends Whiteness. Because of this restriction, S must be an extension of T but also implement the functionality of Whiteness. Because of this, you will likely have to provide the type T. It's not possible for S to have multiple bounded types, which is why a sub-interface is required that implements both. What you're trying to do doesn't make logical sense. Refer to this.
public interface Foo<T extends Blackness, S extends BlackAndWhiteness<T>> {
}
public interface BlackAndWhiteness<T extends Blackness> extends Whiteness, Blackness {
}
interface Blackness {
}
interface Whiteness {
}
I am learning generic types in Java now. To restrict the type argument passed to the type parameter, we use the keyword "extends". It means to either "extends"(as in class) or "implements" (as in interfaces). I don't understand why it can't mean to "extends" the the interface?
Interface only extends another interface ... class implements the interface
the only class that extends the interface will be the abstract class
Yes, you can use an interface when specifying (what's the right term?) a generic type (I had to answer to post the code):
class MyGeneric<T extends MyBaseInterface> {
}
interface MyBaseInterface {
}
interface MySubInterface extends MyBaseInterface {
}
public class GenericsTest {
private MyGeneric<MySubInterface> x = new MyGeneric<>();
}
As I said in my comment above, in <T extends X> "extends" is just a way to require that T is a subtype of X (where X can be a class or an interface).
There's not much to add to my question, basically:
class A {}
interface I {}
// how can I get a Set<> of object of type A that implements I?
I tried a few things <A & I>, <A extends I>, <? super A extends I> and a few other but didn't find anything that works, so I'm wondering if this is possible at all. If it isn't I'm curious about the reasoning behind it.
Thanks
Java does not support intersection types, it only supports multiple bounds (as in extends A & I) when declaring type parameters. That is, we can not use a notation like A & I to denote the family of types that extend both A and I, but we can declare a type parameter <T extends A & I> to refer to a specific such type.
If the latter is what you want, a type parameter is a great fit. But if your collection should admit unrelated subtypes of A and I, no nice solutions seem to exist. My best idea is a hack like:
class AISetWrapper {
Set<A> set = new HashSet<>();
<T extends A & I> Set<T> getSet() {
return (Set<T>) set; // unchecked cast that only works because generics are not reified
}
}
which would allow us to write:
class AI1 extends A implements I { }
class AI2 extends A implements I { }
public static void main(String[] args) {
AISetWrapper aiSet = new AISetWrapper();
aiSet.get().add(new AI1()); // compiles
aiSet.get().add(new AI2()); // compiles
aiSet.get().add(new A()); // does not compile
aiSet.get().add(new I() {}); // does not compile
}
You'll have to make A implement I:
interface I {}
class A implements I {}
Set<A> setOfA;
Possible is alsp
class SubA extends A implements I { }
Set <SubA> setOfSubA;
Usage of a class A cannot make it change it's behaviour, as would be indicated by its sudden "implmentation" of I. Where should the implementations of the interface methods come from?
I was able to do the following:
public class MyClass<T extends String & Iterable>{
private Set<T> mySet;
}
And
public <T extends String & Iterable> void myFancyMethod(Set<T> mySet){}
However when I did
private Set<? extends String & Iterable>
I got a compile error of Syntax error on token "&". Seems that you can do the & syntax when declaring a type <T> but not for wildcards <? ...>.
A better discussion of this can be found at: Java Generics Wildcarding With Multiple Classes
You can write your own class:
public class MySet<E extends A & I> extends HashSet<E> {
// blank
}
This will simply ensure that any instances of MySet will contain only objects that extend A and implement I.
// how can I get a Set<> of object of type A that implements I?
You cannot guarantee both in a single generic statement. You can do something like
public void addToSet(I iInstance) {
if(iInstance instanceof A){
//logic to add to your set
}
}
I would like to decorate a set of classes that derive from a common class (TextView). The classes exist in a library, so I cannot simply modify the base or insert into their inheritance hierarchy.
class A {
}
class B extends A {
}
class C extends A {
}
class D extends B {
void decoration(){}
}
In the example above, class D is the decorating class. The decorating functionality is common to each of my decorating classes.
My question is, is it possible to template the base class in Java? Something like:
class D<T> extends <T ? extends A> {}
So your question is about adding a method dynamically to existing classes? Something similar to categories in Objective-C. This is not simple to be done in Java since once a class is loaded through the ClassLoader you can't add anything dynamically to it.
The easiest thing that comes into my mind is to provide a custom mapping that will be by any chance external to the existing classes. Something like:
interface Method<T extends A> {
public void invoke(T ref);
}
class MethodForA implements Method<A> {
public void invoke(A ref) { .. }
}
class MethodMapper {
Map<Class<?>, Method<? extends A>> mapping;
MethodMapper() {
mapping = new HashMap<Class<?>, Method<? extends A>>();
mapping.put(A.class, new MethodForA());
}
void invoke(A object) {
Method<? extends A> method = mapping.get(object.getClass());
if (method != null) {
method.invoke(object);
}
}
I just wrote this boilerplate code right now so everything won't be surely correct but the way it would work is this one. I guess you will need to do some runtime type checks to avoid blindly casting things.
If instead you were wondering if this is legal:
class A {
}
class B<T> extends A {
}
class C<T, U> extends B<T> {
}
Yes, it is. You can make a child class generic by introducing a type parameter.
I'm having a problem understanding Java generics and I've simplified to this example
class A<T extends B> {
public void fun(T t) {
}
}
class B {
A a;
public void event() {
a.fun(this);
}
}
The problem is that this generates a warning because A is defined inside B but A is already using it as a generic type.
My first instinct would be that my design is wrong, but in this case I can't change it. A is like a collection and B is like a node in the collection that users are supposed to override. Certain events can happen in B that require reporting back to the parent A.
But since A is defined generically with B, how do I avoid the compile warning inside B.event()
Thanks
The problem is that you're using a raw type on this line:
A a;
You need to specify a type for A's type parameter (T).
You could do something like this:
A<B> a;
but then A might as well not be generic at all, if I'm understanding your statement of the problem. You probably want to do something like this:
class A<T> {
public void fun(T t) {
}
}
class B<T extends B<T>> {
A<B<T>> a;
public void event() {
a.fun(this);
}
}
or even this:
class A<T extends B<? extends T>> {
public void fun(T t) {
}
}
class B<T extends B<T>> {
A<? super B<T>> a;
public void event() {
a.fun(this);
}
}
There are a couple of variations in-between these that are possibly useful as well. The latter example is the most generic (but obviously, also the most complicated).
The class A<T extends B<? extends T>> is ensuring that the type parameter to A is a B. Since B is itself generic, and has that cyclic type parameter, you end up needing to say B<? extends T> (simply saying T won't work here).
The class B<T extends B<T>> is as close as you can get to emulating a "self type" in Java. This lets B talk about the (almost) concrete subtype of itself. When subclassing B you'd say something like "class C extends <B<C>>". This is useful because now the type of C.a is actually A<? super B<C>>.
The ? super bit in the latter example is only useful if you plan on connecting a B with an A that isn't for exactly the same type of B. Thinking in concrete terms, suppose you had an A<Shape> and a Circle (which extends Shape which extends B). The super-wildcard lets you use them together. Without it you'd need an A<Circle> rather than an A<Shape> for your Circle.
Code
public class A<T extends B> {
public void fun(T t) {
}
}
public class B {
A<B> a;
public void event() {
a.fun(this);
}
}
The warning is vanquished.
Reason
Variables of type A should be declared using a specific class type, as suggested by the generic class signature (A<T extends B>).
Resolution
While this resolves the compiler warning, the underlying problem remains. Laurence provides an excellent explanation and solution to the core issue.