Generic parameters for mix-in interfaces
I am experimenting with mixin interfaces that add additional functionality to existing interfaces. E.g. I have an interface Filtering
interface Filtering<T, E>{
T filter(Filter<E> f);
}
I can easily add that to existing interfaces:
interface Container<E> extends Filter<Container<E>, E>{
// translated type: Container<E> filter(Filter<E> f);
}
Now I'd also like to have a transforming behavior, that transforms Container<E>
to Container<X>
with the syntax
Container<X> transform(Transformer<E,X> transformer)
Is there any way I can define this functionality as a Mix-in interface, so that I can transform Container<E>
to Container<X>
but also SomeOtherContainer<E>
to SomeOtherContainer<X>
?
interface Transforming< /* what goes in here? >{
< /* and what goes in here? */ > X transform(Transformer<S,T> transformer);
}
interface Container<E> extends Transforming<Container<E>, /* and in here? */ >{
}
Personally, I don't think it's possible, I think I need to add the transform method to each of the target interfaces, but I haven't quite lost hope yet. Can anybody help?
Clarification: The Transformer
function converts the individual E
elements to X
elements. I do not want Transformer
to convert from Container<E>
to Container<X>
, that would be completely useless.
This is how I'd like to use it:
Container<Integer> intVersion = // initialize it
Container<String> hexVersion =
intVersion.transform(new Transformer<Integer,String>(){
public String apply(Integer input){
return Integer.toHexString(input);
}
});
BTW: I know I ca开发者_开发问答n do similar stuff in Guava. Let's ignore that fact for this question, this is not so much about the functionality (which I can easily implement), but about generics usage.
Unfortunately, Java doesn't allow this:
interface Transforming<E, X>
<T> X<T> transform(Transformer<E,T> transformer);
interface Container<E> extends Transforming<E, Container>
Even if it did, it's not general enough; we want a function mapping T
to another type containing T
interface Transforming<E, f>
<T> f(T) transform(Transformer<E,T> transformer);
interface Container<E> extends Transforming<E, {T->Container<T>} >
But enough of fantasies. The best you can do, is to be very vague on return type:
interface Transforming<E>
<X,T> X transform(Transformer<E,T> transformer);
interface Container<E> extends Transforming<E>
Note, we are unable to express any constraint between X and T.
Now your sample code compiles, without any warning!! There is a type inference based on the assignment, and X
is infered to be Container<String>
.
On one hand, the type safety of this inference entirely depends on programmer supplying the correct target type. If he had put Container<Rope>
on the left hand, it will compile too without warning.
On the other hand, if Java does not do such inference, then we must return Object
instead of X
, and do manual cast on return object; the safety of the manual cast of course also depends entirely on programmer supplying correct target type. Some people therefore argue, why are we punishing ourselves? If I assign A to B, of course A is a B, don't force me to write it out, infer it!
Nonetheless, such inference may give false sense of security to casual observers. There is a manual cast, in spirit, but not in writting. Personally I'm quite concerned with this inference rule. It is against the very point of static typing, that is, we want to write down all the types explicitly.
You could try (I'm not completely confident in the syntax, but I think it's close):
interface Transforming<X> {
<Y> Y transform(Transformer<X, Y> transformer);
}
interface Container<E> extends Transforming<Container<E>> {
}
Then your Container will have the Transforming functionality. The type Y will be determined at the actual call to transform, based on what Y is for the Transformer object passes to transform.
This will not guarentee the Y is necessarily a Container type, but maybe something in Transformer can give that restriction.
精彩评论