overload method with same generic parameter? - java

I know that I can't do this:
public abstract class DTODomainTransformer<T, S> {
public abstract S transform(T);
public abstract T transform(S);
}
Because I get the compiler complaint:
Method transform(T) has the same erasure transform(Object) as another method in type Transformer<T,S>
I understand that is because both T and S could be extending same class. So doing this way i can tell him "No, they are not the same, so take it easy"
public interface Transformer<T extends AbstractDTO , S extends AbstractDomain> {
public abstract S transform(T object);
public abstract T transform(S object);
}
Then, my question is, is there any way to tell the compiler that T and S extend from different classes without telling which ones in concrete? I mean, in this last case, I've specified which classes had to be T and S (extending respectively). But what if I want it more generic and not specify them? I'd like to tell the compiler, "Hey, compiler, T and S are not the same! They are different classes. I don't know exactly which classes they are, but I'm sure that they are different".

There's not an obvious way. (Although you can build one, as I show below.)
This overload rule is due to a limitation of how the supertype (in this case, interface) that declares the overloads gets translated (by erasure) to bytecode.
If there's a generic parameter declared T, a method that uses T in its signature will have bytecode generated as the upper bound of T, for example
class Generic<T> {
void work(T t) {}
}
will get erased to
class Generic {
void work(Object t) {}
}
and
class Generic<T extends Number> {
void work(T t) {}
}
will get erased to
class Generic {
void work(Number t) {}
}
This is how the bounded example works, because the overloads erase differently.
public interface Transformer {
public abstract AbstractDomain transform(AbstractDTO object);
public abstract AbstractDTO transform(AbstractDomain object);
}
But without specific bounds, what erased bytecode should be generated for the overloaded methods?
So your T and S being different on the subtype is not what's important. What is important is the known declared bounds which get translated to erased bytecode for the supertype class.
A possible solution could use marker interfaces.
interface TransformT {}
interface TransformS {}
interface Transformable extends TransformT, TransformS {}
interface Transformer<T extends TransformT, S extends TransformS>
T transform(S s);
S transform(T t);
}
abstract class AbstractDTO implements Transformable {}
abstract class AbstractDomain implements Transformable {}
new SomeTransformerImpl<AbstractDTO, AbstractDomain>()
But I don't necessarily recommend doing this. It seems elaborate to me, although interesting. It depends on how complicated the actual class hierarchy is.
What Louis suggested in the comments is much simpler: give the methods different names.

Related

Java inheritance; passing a subclass to an abstract method of a superclass

Sorry for the title, couldn't come up with anything clearer.
I have the following structure:
public interface Vehicle {...}
public class Car implements Vehicle {...}
then:
public abstract class Fixer {
...
abstract void fix(Vehicle vehicle);
...
}
and would like to have:
public class CarFixer extends Fixer {
void fix(Car car) {...}
}
but this doesn't work. Eclipse says: The type CarFixer must implement the inherited abstract method Fixer.fix(Vehicle). Any idea how can I solve this?
You can use Generics to solve this:
public abstract class Fixer<T extends Vehicle> {
abstract void fix(T vehicle);
}
public class CarFixer extends Fixer<Car> {
void fix(Car car) {...}
}
The problem with your original version is that the fix method allows any type of vehicle, but your implementing class allows only cars. Consider this code:
Fixer fixer = new CarFixer();
fixer.fix(new Bike()); // <-- boom, `ClassCastException`, Bike is a vehicle but not a car
You've met the humble home of generics.
Generics provide kind of 'wildcard' type where a class or method can specify that 'we don't really care what type it is, we just need -a- type'.
Generics allow a super class to enforce a specific type in a child class instead of allowing any class that extends a certain class.
This means that you're ultimately enforcing a new highest allowed super-class as the parameter (i.e. Vehicle is no longer the most basic allowable type you can pass to fix(); it's now whatever the subclass says it is, so long as that arbitrary type extends Vehicle).
Common places for generics are container classes (i.e. List, Map, and Set) where the container doesn't really care about what type it tracks, but rather focuses on actually tracking and managing those instances.
Generics consist of one or more type placeholders (in Java, E and T are commonly used but the name doesn't really matter; they usually follow the normal type naming conventions) that are used in place of a specific class or super class.
In your code, you want subclasses to implement methods given their exact relevant types (i.e. a CarFixer would take Cars, a JetpackFixer would take Jetpacks) but you want to enforce that these types extend Vehicle.
In order to enforce this, you have to tell the Fixer class exactly what your subclass wants.
public abstract class Fixer <E extends Vehicle>
{
abstract void fix(E vehicle);
}
Your subclass then extends Fixer, filling in E with the type it wants.
public class CarFixer extends Fixer<Car>
{
#Override
void fix(Car vehicle)
{
// ...
}
}

Inheriting from a generic interface multiple times while respecting variance

I ran into a problem when trying to specialize a class that implements a generic interface where I wanted to inherit from the same interface as the super class, but with a more specific type argument. The following snippet shows a synthetic but complete example that cannot be compiled. The comment contains the error message from the Java compiler.
interface Producer<T> {
T get();
}
class NumberProducer implements Producer<Number> {
#Override
public Number get() { return null; }
}
// Producer cannot be inherited with different arguments: <java.lang.Integer> and <java.lang.Number>
class IntegerProducer extends NumberProducer implements Producer<Integer> {
#Override
public Integer get() { return null; }
}
In the PECS sense, Producer<T> is a producer, so Producer<Integer> would be a subtype of Producer<Number>, but there's no way to declare that in the definition of Producer<T>. Java does not allow IntegerProducer to inherit from NumberProducer and Producer<Integer> at the same time as IntegerProducer would then inherit from Producer<Integer> and Producer<Number> at the same time.
Is there a standard approach to this limitation, e.g. a pattern that solves the same problem without requiring this kind of inheritance?
Just add a parameter to the super class:
interface Producer<T> {
T get();
}
class NumberProducer<T extends Number> implements Producer<T> {
#Override
public T get() { return null; }
}
class IntegerProducer extends NumberProducer<Integer> { // Implicit: implements Producer<Integer>
#Override
public Integer get() { return null; }
}
Say we had a simple Method gimme.
public static <T> T gimme(Producer<T> p) { return p.get(); }
Within the context of gimme nothing is known about T. It could be Number, Integer or any other reference type. So, due to erasure, the compiler emits an interface call to Producer.get()Object rather than the specific call to, say, IntegerProducer.get()Integer. All types that implement Producer<T> with T != Object also implicitly implement Producer.get()Object. This implicit implementation forwards to the specific implementation. That might be NumberProducer.get()Number or IntegerProducer.get()Integer, but not both. That's why you can't implement the same interface twice.
Other languages allow this via definition site variance, where Producer<Integer> is a subtype of Producer<Number>, but alas, Java does not. The common workaround is to make NumberProducer generic as well.
If you want to ensure that T is of a specific subtype you can use
interface Producer<T extends Number>
Not sure what Producer reall is so I have to guess.
Update:
If I understand you correct then I would say, you need to declare an interface which is a Producer. That's simple.
From this interface i would derive a new interface with the respecitve base type.
i.e.:
interface Producer
{
base functions
};
interface Newproducer<T extends Producer>
{
};
Is this what you had in mind?

Java Generics Hell

I suspect this has been asked here (and answered) before, but I don't know how to name the problem. Why can I express the wildcards without problem only when I'm not passing the class itself?
It all boils down to this code. Everything works as expected except for the call to genericsHell(ShapeSaver.class):
interface Shape { }
interface Circle extends Shape { }
interface ShapeProcessor<T extends Shape> { }
class CircleDrawer implements ShapeProcessor<Circle> { }
class ShapeSaver<T extends Shape> implements ShapeProcessor<T> { }
class Test {
void genericsHeaven(ShapeProcessor<? extends Shape> a) {}
void genericsHell(Class<? extends ShapeProcessor<? extends Shape>> a) {}
void test() {
genericsHeaven(new CircleDrawer());
genericsHeaven(new ShapeSaver<Circle>());
genericsHell(CircleDrawer.class);
genericsHell(ShapeSaver.class); // ERROR: The method genericsHell is not applicable for the arguments (Class<ShapeSaver>)
}
}
The type of ShapeSaver.class is Class<ShapeSaver>. When feed it to genericsHell(), compiler needs to check if Class<ShapeSaver> is a subtype of Class<? extends ShapeProcessor<?>, which is reduces to whether ShapeSaver is a subtype of ShapeProcessor<?>. The subtype relation does not hold, the method call fails.
The same thing should be true for #Bohemian's solution. Here the subtype checking occurs at bound checking of T after T is inferred. It should fail too. This appears to be a compiler bug, which somehow misinterprets the rule that Raw is assignable to Raw<X> as if Raw is a subtype of Raw<X>. see also Enum.valueOf throws a warning for unknown type of class that extends Enum?
A simple solution to your problem is to declare
void genericsHell(Class<? extends ShapeProcessor> a)
indeed, ShapeSaver is a subtype of ShapeProcessor, and the call compiles.
That's not just a workaround. There's a good reason for it. Strictly speaking, for any Class<X>, X must be a raw type. For example, Class<List> is ok, Class<List<String>> is not. Because there is really no class that represents List<string>; there is only a class representing List.
Ignore the stern warning that you shall not use raw type. We must use raw types sometimes, given how Java type system is designed. Even Java's core APIs (Object.getClass()) use raw types.
You probably intended to do something like this
genericsHell(ShapeSaver<Circle>.class);
Unfortunately, that's not allowed. Java could have, but did not, introduce type literal along with generics. That created lots of problems for lots of libraries. java.lang.reflect.Type is a mess and unusable. Every library has to introduce their own representation of type system to solve the problem.
You can borrow one, e.g. from Guice, and you'll be able to
genericsHell( new TypeLiteral< ShapeSaver<Circle> >(){} )
------------------
(learn to skip the craps around ShaveSaver<Circle> when reading the code)
In the method body of genericsHell(), you'll have full type information, not just the class.
Typing the genericsHell method allows it to compile:
static <T extends ShapeProcessor<?>> void genericsHell(Class<T> a) {}
EDITED: This allows the compiler to specify from context, or by coding an explicit type, that the ShapeProcessor is not literally any ShapeProcessor, but the same type as the one passed as a parameter. If the call was explicitly typed, (which the compiler does under the covers) the code would look like this:
MyClass.<ShapeSaver>genericsHell(ShapeSaver.class);
Which interestingly, gives a type warning, but still compiles. The explicit type is not required however because sufficient type information is available from the parameter to infer the generic type.
Your question is missing some declarations, so I added them in to create a Short Self-Contained Correct Example - ie this code compiles as-is
static interface Shape { }
static interface Circle extends Shape { }
static interface ShapeProcessor<T extends Shape> { }
static class CircleDrawer implements ShapeProcessor<Circle> { }
static class ShapeSaver<T extends Shape> implements ShapeProcessor<T> { }
static void genericsHeaven(ShapeProcessor<? extends Shape> a) { }
// The change was made to this method signature:
static <T extends ShapeProcessor<?>> void genericsHell(Class<T> a) { }
static void test() {
genericsHeaven(new CircleDrawer());
genericsHeaven(new ShapeSaver<Circle>());
genericsHell(CircleDrawer.class);
genericsHell(ShapeSaver.class);
}

For java,how to make sure inner interface and outer interface have same generic type?

Version 1
public interface Outer<T>{
public interface Inner<T>{
T get();
void set(T t);
}
}
Although same generic type syntax ,these two type are totally independent.
Version 2
public interface Outer<T>{
public interface Inner<V extends T>{
V get();
void set(V t);
}
}
Got an error:"Cannot make a static reference to the non-static type T"
Version 3
public interface Outer<T>{
public interface Inner{
<T> T get();
<T> void set(T m);
}
}
Not sure whether this is what I want, but seems fine (no errors in eclipse), so I try to implement it:
public Test implements interface Outer.Inner {
//no problem in these two
<T> T get(){..};
<T> void set(T m){...};
//Errors come up here:
Map<String,T> map;
public Test(Map<String,T> map){
this.map=map
}
}
the error comes up:
"T cannot be resolved to a type" in both
declaration Map<String,T> map
constructor argument public Test(Map<String,T> map){}
so,how can I solve this problem?
Firstly, there is no way to do this, since the outer interface is nothing more than a "namespace" for the inner interface. They really have nothing else to do with each other.
This begs the question: why would you need to have such a control over things? If, in the class which implements the interface, it is decided that using the same type makes sense, then it will be so for that class. But another implementation could, and should be allowed to, choose otherwise. This is especially true since classes which implement Outer and those implementing Inner are not restricted to being nested with each other.
The source of your problem is this ; Inner interface is static , therefore it is not related any instanceof Outer interface. Therefore you can not use, Outer interface generic type for Inner interface..
According to JLS ;
9.5 Member Type Declarations
Interfaces may contain member type
declarations (ยง8.5). A member type
declaration in an interface is
implicitly static and public
What you should do is, possibly, have Inner extends Outer, if the type must absolutely be enforced, but of course, you'll have to implement methods of both Inner and Outer. I'm not sure you want this.
interface Outer<T> {
interface Inner<T> extends Outer<T> {
T myMethod(T t);
}
}
There isn't really another way to enforce what you want. The reason is that an interface is always static. Also, you don't really want/need this behavior: an interface is already flexible enough for you. Just learn where you may have control over this and the world will be yours!
Error in implementation - you foget to add generic type, this should help:
public class Test<T> implements interface Outer<T>.Inner {
T get(){..};
void set(T m){...};
//Errors come up here:
Map<String,T> map;
public Test(Map<String,T> map){
this.map=map
}
}

Overriding a method with Generic Parameters in Java?

I have an abstract Class Monitor.java which is subclassed by a Class EmailMonitor.java.
The method:
public abstract List<? extends MonitorAccount> performMonitor(List<? extends MonitorAccount> accounts)
is defined in Monitor.java and must be overridden in EmailMonitor.java.
I currently have the method overridden in EmailMonitor.java as follows:
#Override
public List<EmailAccount> performMonitor(List<EmailAccount> emailAccounts) {
//...unrelated logic
return emailAccounts;
}
However, this produces the compile time error:
Name clash: The method performMonitor(List<EmailAccount>) of type EmailMonitor has the same erasure as performMonitor(Lis<? extends MonitorAccount> emailAccounts) of type Monitor but does not override it
EmailAccount is a subclass of MonitorAccount, so (in my mind at least) overriding it in this way makes perfect sense. Seeing as the compiler is not happy with my logic though, How should I go about this correctly while still keeping my compile time checks to make sure that all calls to EmailMonitor.performMonitor() receive Lists of EmailAccount rather than some other type of MonitorAccount?
No, it's not overriding it properly. Overriding means you should be able to cope with any valid input to the base class. Consider what would happen if a client did this:
Monitor x = new EmailMonitor();
List<NonEmailAccount> nonEmailAccounts = ...;
x.performMonitor(nonEmailAccounts);
There's nothing in there which should give a compile-time error given your description - but it's clearly wrong.
It sounds to me like Monitor should be generic in the type of account it can monitor, so your EmailMonitor should extend Monitor<EmailAccount>. So:
public abtract class Monitor<T extends MonitorAccount>
{
...
public abstract List<? extends T> performMonitor(
List<? extends T> accounts);
}
public class EmailMonitor extends Monitor<EmailAccount>
{
#Override
public abstract List<? extends EmailAccount> performMonitor(
List<? extends EmailAccount> accounts)
{
// Code goes here
}
}
You might want to think carefully about the generics in the performMonitor call though - what's the return value meant to signify?
Here is my own solution. I suspect this is the same thing Jon Skeet was trying to get at... without the typo (see my comment in reply to his answer).
the Monitor.java class:
public abstract class Monitor <T extends MonitorAccount> {
...
public abstract List<T> performMonitor(List<T> accounts);
..
}
EmailMonitor.java
public class EmailMonitor extends Monitor<EmailAccount> {
...
public List<EmailAccount> performMonitor(List<EmailAccount> emailAccounts) {
..//logic...logic...logic
return emailAccounts;
}
...
}
In this configuration, EmailMonitor.performMonitor() will always check at compile time that it receives a list of EmailAccount rather than any of my other types FTPAccount, DBAccount, etc... It's much cleaner than the alternative, which would have been receiving/sending a raw list and then having to coerce it the required type resulting in potential runtime type casting exceptions.

Categories