I'm trying to extend an abstract class with generics and I'm running into a problem:
abstract public class SomeClassA<S extends Stuff> {
protected ArrayList<S> array;
public SomeClassA(S s) {
// ...
}
public void someMethod() {
// Some method using the ArrayList
}
abstract public void anotherMethod() {
// ...
}
}
Now I want to extend this class with another abstract class so I could override "someMethod". I tried:
abstract public class SomeClassB<Z extends Stuff> extends SomeClassA {
public SomeClassB(Z z) {
super(z);
}
#Override public void someMethod() {
// Some method using the ArrayList
}
}
NetBeans doesn't see any problem with the constructor, but I cannot use the ArrayList from SomeClassA within the method someMethod. So I tried:
abstract public class SomeClassB<Z extends Stuff> extends SomeClassA<S extends Stuff> {
public SomeClassB(Z z) {
super(z);
}
#Override public void someMethod() {
// Some method using the ArrayList
}
}
And now it's just very odd. Everything seems to work (and I can now use the arraylist, but NetBeans says there's a "> expected" in the declaration of SomeClassB and it just won't compile. If possible, I would like:
To know how to solve this particular problem.
To have a good reference to understand generics.
To know if it's any easier in C#.
You will need to pass the generic type to the superclass also, like this:
abstract public class SomeClassB<Z extends Stuff> extends SomeClassA<Z>
Your superclass and subclass will then both use the same generic type. Generic Types are not inherited by subclasses or passed down to superclasses.
For a good reference to understand generics, check out Effective Java, 2nd Edition.
Related
I have following base class
class Base<T> {
// ...
}
and I have this class to extend the Base
class Derived extends Base<String> {
public void doSomething(String param) { // this works
}
public void doSomething(Base::T param) { // this DOES NOT work
}
}
I want know whether there is a java trick to get the second function definition to work without using the concrete class String directly?
Yes I have a reason. There are many classes need extend the Base with different concrete type. I want to avoid change the funciton signature in each class, this should be avoided if possible.
If you have many classes that need to extend the Base<T> class and they all have a doSomething method, you should probably put the doSomething in Base<T>:
class Base<T> {
public void doSomething(T param) {} // or even make this abstract
}
And then, IDEs like IntelliJ IDEA can generate the overridden method for you. When you start typing the method name, this pops up:
You press enter, and then it will be generated for you.
#Override
public void doSomething(String param) {
super.doSomething(param);
}
I suspect that you might be copy and pasting the implementation of doSomething, because otherwise why you think it is annoying to change the parameter type for each implementation?
If the implementations are mostly the same, consider having the doSomething method in the Base<T>, then extract the parts that are different into a doSomethingImpl method which will be overridden by the derived classes.
In order to pass different parameters to your method, you can implement an upper bounded type parameter.
class Derived<T extends Foo> extends Base<Foo> {
public void doSomething(Foo param) { // this works
}
}
public <T extends Foo> void doSomething(List<T> foos) {}
public void doSomething(List<Foo> foo) {}
Given: class Bar extends Foo { }
List<Bar> list = new ArrayList<Bar>();
doSomething(list);
Suppose I have the following four classess, out of which two are abstract and two are concrete: Abstract1, Concrete1, Abstract2, Concrete2.
Concrete1 extends Abstract1 and Concrete2 extends Abstract2.
Suppose I have the following four classess, out of which two are abstract and two are concrete: AbstractProp, ConcreteProp, AbstractClass, ConcreteClass.
ConcreteProp extends AbstractProp and ConcreteClass extends AbstractClass.
The code for AbstractClass would look like the following:
public abstract class AbstractClass {
protected AbstractProp someProperty;
public abstract void setProperty(AbstractProp someProperty);
}
When implementing ConcreteClass, I want to be able to do something like this (keep in mind that AbstractProp extends `ConcreteProp):
public class ConcreteClass extends AbstractClass {
#Override
public void setProperty(ConcreteProp someProperty) {
this.someProperty = someProperty;
}
}
But this last bit of code isn't valid because the classes of the parameters (AbstractProp and ConcreteProp) in setProperty() differ, making the #Override annotation invalid. Is there a way I can accomplish what I'm trying to do here, perhaps using generics and/or wildcards?
NOTE: I'm aware that this type of naming is bad practice and the code is oversimplistic, I'm not actually using it and this is just an example to keep the question as simple as possible.
If you introduce generics on the AbstractClass you can achieve what you are requesting.
public abstract class AbstractClass<T extends AbstractProp> {
protected T someProperty;
public void setProperty(T someProperty) {
this.someProperty = someProperty;
}
}
And then specify that you want ConcreteProp to be the type you want in your ConcreteClass.
public class ConcreteClass extends AbstractClass<ConcreteProp> {
#Override
public void setProperty(final ConcreteProp someProperty) {
this.someProperty = someProperty;
}
}
Consider the following code
#Test
public void testFunction() {
// This cause error
callDoSomething(new myInterfaceImpl());
}
public interface myInterface {
int doSomething();
}
public class myInterfaceImpl implements myInterface {
public int doSomething() {
return 1;
}
}
public void callDoSomething(Class<? extends myInterface> myVar) {
System.out.println(myVar.doSomething());
}
On this line callDoSomething(new myInterfaceImpl()); I get the following error.
Error:(32, 25) java: incompatible types: com.myProject.myTest.myInterfaceImpl
cannot be converted to java.lang.Class<? extends com.myProject.myTest.myInterface>
How do I satisfy the parameter type? If only an interface is provided to me.
I want to bound the class that has an interface, but it seems like this is not avaiable to me
Class<? implements myInterace>
Edit:
The reason I want to do this is because I want to provide a custom kafka partitioner.
public Builder<K, V> withCustomPartitionner(Class<? extends Partitioner> customPartitioner) {
this.customPartitioner = customPartitioner;
return this;
}
It looks like you want to be able to call methods on the parameter that's given. In that case, you'll want the actual instance of your interface, not the Class associated with it.
public void callDoSomething(myInterface myVar) {
System.out.println(myVar.doSomething());
}
Class<> is used when you want to use reflection to do something with the specific class type that you're interested in:
public void outputClassInfo(Class<? extends myInterface> myClass) {
System.out.println(myClass.getName());
}
If that's what you're going for, you'll want to provide the class at compile time like this:
outputClassInfo(myInterfaceImpl.class);
Or, if you won't know which class you're dealing with until runtime, you can use reflection:
myInterface thing = getThing();
outputClassInfo(thing.getClass());
So, in the example you're providing in your edit, I'm guessing you want:
public Builder<K, V> withCustomPartitioner(Class<? extends Partitioner> customPartitioner) {
this.customPartitioner = customPartitioner;
return this;
}
// Usage
builder
.withCustomPartitioner(FooPartitioner.class)
...
This type Class<? extends myInterface> myVar corresponds to a Class instance not to an instance of myInterface.
You generally don't pass a class as parameter (but for reflection purposes or to bypass generics erasures). So what you need as parameter is probably :
public void callDoSomething(myInterface myVar) {
System.out.println(myVar.doSomething());
}
That you could invoke :
#Test
public void testFunction() {
// This cause error
callDoSomething(new myInterfaceImpl());
}
The parameter to callDoSomething shouldn't be a class. It must be an instance of that class or it's subclass.
public <T extends myInterface> void callDoSomething(T myVar) {
System.out.println(myVar.doSomething());
}
On a side note, don't name Java classes/interfaces starting with lower case.
As rightly mentioned by Andy Turner#, there is no need to use a type parameter here and you can just refer to the type as myInterface
public void callDoSomething(myInterface myVar) {
System.out.println(myVar.doSomething());
}
You need to pass the Class not an instance.
callDoSomething(MyInterfaceImpl.class);
I want to override a method and replace a parameter it takes with a subclass of that parameter.
For return type there is no problem, because they are not a part of method signature and can be replaced with subclasses (called "covariant return type"). For arguments this is not working, because they are a part of signature.
So I came out with solution to use generic parameter of the same type:
public abstract class A<T extends A> {
public void loadValuesFrom(T source) {
...
}
}
public class B extends A<B> {
public void loadValuesFrom(B source) {
super.loadValuesFrom(source);
...
}
}
But the statement "public abstract class A" looks odd to me. Are there any other ways to achieve this? Any "covariant parameter type"? :-)
If it must be a parameter type, using generics is probably the best option. I'd only do a minor correction to avoid the raw type in the class declaration:
public abstract class A<T extends A<T>> {
If however you have the special case where the object being initialized needs to be freshly created, you might include creation in a method, thereby removing the need to pass that object as parameter:
public abstract class A {
public A clone() {
}
}
public class B extends A {
public B clone() {
// copy state
}
}
public abstract class AbstractTool<AT extends AbstractThing> {
protected ArrayList<AT> ledger;
public AbstractTool() {
ledger = new ArrayList<AT>();
}
public AT getToolAt(int i) {
return ledger.get(i);
}
// More code Which operates on Ledger ...
}
public class Tool<AT extends AbstractThing> extends AbstractTool {
public Tool() {
super();
}
}
How do I correctly call super to pass the AT generic of Tool to the AbstractTool constructor?
It seems no matter what I pick AT to be when I declare Tool (Say, Tool<Thing>), that I always get back an AbstractThing instead of Thing. This seems to defeat the purpose of generics...
Help?
public class Tool<AT extends AbstractThing> extends AbstractTool<AT> {
In other words, if you extend or implement something with generics, remember to define the generics arguments for them.
Shouldn't it rather be
Tool<AT extends...> extends AbstractTool<AT>?
I think what you probably want is:
public abstract class AbstractTool<AT extends AbstractThing> {
protected List<AT> ledger = new ArrayList<AT>();
public AT getToolAt(int i) {
return ledger.get(i);
}
// More code Which operates on Ledger ...
}
public class Tool extends AbstractTool<Thing> {
// Tool stuff ...
}
Since Tool is a concrete class, it doesn't need to be parametrized itself. There is no need for the constructors if you initialize the List (oh and remember to program to the interface) at declaration, and because it is protected the subclasses can access it directly.