i have a problem with an Interface and generic setters that i tried to solve for some hours now.
i have an interface where i want to define some getter and setter functions. the getter functions should be implemented by some abstract class since they usually shouldn't change.
the setter functions on the other hand should be overrideable multiple times by a defined class. In the case i try to describe it would be that the childClass should be able to implement 2 setFunctions with the same name and different input values
Interface TestClass {
public abstract void setSomething(List<?> value);
public abstract List<String> getSomething();
}
abstract class AbstractTestClass implements TestClass {
List<String> someData;
public List<String> getSomething() {
return someData;
}
}
class TestClassImplementation extends AbstractTestClass() {
#Override
public void setSomething(List<String> data) {
someData = data;
}
#Override
public void setSomething(List<SomeOtherType> data) {
someData = convertToStringList(data);
}
private List<String> convertToStringList(List<SomeOtherType> data) {
... do some conversion ...
return returnList;
}
}
hope this gives the idea of what i want to do. I would even prefer to implement the setSomething with the stringlist in the abstract class. But both setters must be reachable.
Thanks
You simply can't do that. Generics are not retained at runtime (google type erasure for more infos on this or just read the wikipedia page).
This means that your setSomething-methods all have the same signature, as their only parameter is of type List.
Well, you should try with generic solution:
Interface TestClass<T> { //Generic type T that you will provide when extending with actual class
public abstract void setSomething(List<T> value);
public abstract List<T> getSomething();
}
class TestClassImplementation extends AbstractTestClass<RealType> {
#Override
public void setSomething(List<RealType> data) {
someData = data;
}
}
The thing that confuses you is that the wildcard sign ? does not mean it changes any type, it just denotes an unknown type.
Related
In my project, I have multiple services performing three basic operations - create, edit and search. For this, I am trying to create a generic service. Below is what I have come up with so far.
Search method will take a list of objects at runtime.
public interface GenericService<T> {
void update(T t);
void create(T t);
T search(List<?> t);
}
Also, I have created an abstract class where the common code for all services will be placed.
public abstract class AbstractService<T> implements GenericService<T> {
}
Here is my implementation
public class AccountService extends AbstractService<Account> implements GenericService<Account> {
#Override
public void update(Account account) { }
#Override
public void create(Account account) { }
#Override
public Account search(List<SearchCriteria> t) { return null; }
}
Here are my Account and SearchCriteria classes
public class Account {
private String accountNumber;
private Date openingDate;
// more fields
// getter setter removed for brevity
}
Search criteria class
public class SearchCriteria {
private String key;
private String value;
// getter setter removed for brevity
}
Problem: on line public Account search(List t) { return null; }, getting compilation error saying
'search(List)' in
'com.test.AccountService' clashes with
'search(List)' in 'com.test.GenericService';
both methods have same erasure, yet neither overrides the other
In order for
public Account search(List<SearchCriteria> t) { ...}
to override
T search(List<?> t);
The arguments must be the same after type parameter substitution, but ? is not SearchCriteria.
Therefore, if you want to keep these methods (the inheritance looks a bit wild to me), you'll need to parameterise the types further.
public interface GenericService<T, C> {
// ...
T search(List<C> t); // probably change that parameter name
}
public abstract class AbstractService<T, C>
implements GenericService<T, C>
{
}
public class AccountService
extends AbstractService<Account, SearchCriteria>
implements GenericService<Account, SearchCriteria> // unnecessary
{
// ...
#Override
public Account search(List<SearchCriteria> t) { /* ... */ }
}
Changing List<?> to List<SearchCriteria> in GenericService will solve the error. There is no benefit in using a wildcard if the search method will always take a list of SearchCriteria objects in every service implementation.
If, however, you want to make this generic as well, you can introduce a second type parameter.
The Java documentation for wildcards says that they can be used as return types but it is generally not a good idea. Is this still true if the wildcard is used as the return type for an abstract method but the class that implements this method returns a concrete type? If not, what is the best way to handle this type of situation. Consider the example below. In this case, Entity might be modeled after a JSON REST response, where in the second case the result is just a list of Strings. Is it better to use List<Object> as the return type or something else entirely?
public abstract class AbstractClient {
public abstract List<?> listEntities();
}
public class ConcreteClient {
#Override
public List<Entity> listEntities();
}
public class ConcreteClient2 {
#Override
public List<String> listEntities();
}
In such a case it is better to use generics properly instead of using wildcards. Use a type parameter and extend the abstract class with the appropriate argument for the type parameter:
public abstract class AbstractClient<T> {
public abstract List<T> listEntities();
}
public class ConcreteClient extends AbstractClient<Entity> {
#Override
public List<Entity> listEntities();
}
public class ConcreteClient2 extends AbstractClient<String> {
#Override
public List<String> listEntities();
}
I have a similar requirement to this question.
I would like to generify a method but restrict the types the acceptable by the generic parameter. Currently what I do is attempt to cast to the acceptable types in the method but seems cumbersome if dealing with more than 2 or 3 types.
EDIT:
The types may not be of the same base class. Apologies for not mentioning this earlier.
For this, You must have a base class so that you can do this.
public class Person {
String name;
List<Profession> professions;
int age;
}
public class Doctor {
String university;
Boolean doctorate;
public void work() {
// do work
}
}
public class Teacher {
List<Grade> grades;
float salary;
public void work() {
// do work
}
}
public class Animal<T> {
T type;
}
So, now if you want to write a method which is generic and applies to all, You can do something like this,
public void doSomething(Animal<T extends Person> human) {
human.work();
}
If the class is not of type Person, it will show a compilation error.
UPD1:
In the case, all the classes do not have a common base class. There is some functionality that makes them unique. By this, we can consider them to have a common function, which we can and should add using an interface.
Let's look at some code,
public class Human implements Growable {
public void grow() {
// human grow code
}
}
public class Plant implements Growable {
public void grow() {
// plant grow code
}
}
public class Table {
// does not grows
}
public class GrowService {
public static void grow(Growable growable) {
growable.grow();
}
}
interface Growable {
public void grow();
}
And by calling the below method, we can achieve this
// Works fine
GrowingService.grow(new Plant());
// throws compilation error
GrowingService.grow(new Table());
Java Generics allow basic wildcards such as <T> but also more specifics like
<T extends Number> which means any type T that is Number or a subclass of it or
<T super Number> which means T can be Number or any superclass of Number all the way up to Object.
I've got generic interface Operand:
public interface Operand<T extends Operand<T>> {
T add(Operand<T> op);
T sub(Operand<T> op);
T mul(Operand<T> op);
T div(Operand<T> op);
}
which is implemented by 2 classes:
public class DoubleOperand implements Operand<DoubleOperand> {
...
}
public class RationalOperand implements Operand<RationalOperand> {
...
}
I've written "factory" interface implemented by DoubleOperandFactory and RationalOperandFactory classes:
public interface OperandFactory {
<T extends Operand<T>> Operand<T> valueOf(String s);
}
public class DoubleOperandFactory implements OperandFactory{
#Override
public Operand<DoubleOperand> valueOf(String s) { ... }
}
public class RationalOperandFactory implements OperandFactory {
#Override
public Operand<RationalOperand> valueOf(String s) { ... }
}
In my program I use an enum which objects store instance of the particular Factory:
public enum OperandType {
DOUBLE(new DoubleOperandFactory()), RATIONAL(new RationalOperandFactory());
private OperandFactory fact;
OperandType(OperandFactory fact){ this.fact = fact; }
public OperandFactory getFact() { return fact; }
}
Now I'm getting message warning "Unchecked overriding" on both factory classes, due to unchecked conversion and I'm pretty confused about this. First thing which I tried is (as it was most commonly answered here in similar topics) to replace generic method with generic factory interface, but finally it turns into impossibility to use them with the enum which should return particular factory, without <?>, as I have to create a List of operands (of generic type T extends Operand<T> for now) and fill it with the objects produced by selected factory. What would be the best way to reorganize this code?
public interface OperandFactory {
<T extends Operand<T>> Operand<T> valueOf(String s);
}
This interface does not do what you think it does.
This interface advertises that an instance of it can generate any type of operand you want. It doesn't just generate one particular kind of operand. It generates all of them.
This is not what you actually have.
What you need instead is
public interface OperandFactory<T extends Operand<T>> {
T valueOf(String s);
}
...and then to give up on the enum, because you can't have enums that have different types in them like that. That's just not a thing you're allowed to do in Java.
So, let's say we have a simple interface
public interface ICopyable<T> {
void copyFrom(T original);
}
Which basically adds a copyFrom method with a parameterized type. And you have an interface extending it:
public interface ISomeObject<T> extends ICopyable<T> {
String getType();
}
The interface isn't adding anything valuable of course apart of it's type, but let's pretend there are some useful methods in it. The idea is still the same - all objects of this interface have a getType() method and they can copy one object of THE SAME TYPE from one to the other.
Now let's have two implementations of this interface, and the second implementation inherits from the first one:
public static class ActualObject1 implements ISomeObject<ActualObject1> {
Object data1;
#Override public void copyFrom(final ActualObject1 original) {
this.data1 = original.data1;
}
#Override public String getType() {
return this.getClass().getSimpleName();
}
}
public static class ActualObject2 extends ActualObject1 {
Object data2;
#Override public void copyFrom(final ActualObject1 original) {
super.copyFrom(original);
// oh no! i've just realized that i'm not copying the ActualObject2!
}
}
So the second's object(ActualObject2) is supposed to extend the ActualObject1, but if it's done this way it can't implement the right "copyFrom" method, as the first class implements the interface ISomeObject for itself only. And it obviously wants to do it somehow to allow copying of ActualObject2's to each other. But how?
It can't just declare implements ISomeObject as it'll clash with it's parent's implementation type.
So you'll want to do something like that maybe?
public static class ActualObject1<T extends ActualObject1> implements ISomeObject<T> {
Object data1;
#Override public void copyFrom(final ActualObject1 original) {
this.data1 = original.data1;
}
#Override public String getType() {
return this.getClass().getSimpleName();
}
}
public static class ActualObject2 extends ActualObject1<ActualObject2> {
Object data2;
#Override public void copyFrom(final ActualObject2 original) {
super.copyFrom(original);
this.data2 = original.data2;
}
}
Basically parameterizing the class1, and class2 specifies itself as a parameter. It all works fine, you can create instances of both types:
ActualObject1 obj1 = new ActualObject1();
However there's one "little" problem - the obj1 has a raw type. The full declaration looks rather silly:
ActualObject1<ActualObject1> obj2 = new ActualObject1<>();
But it works. However the "raw type" nature of this class can bite for example in this scanario:
public static class SomeOtherParameterizedClass<T extends ISomeObject<T>> {
void copyObjects(T obj1, T obj2) {
obj1.copyFrom(obj2);
}
}
So you're creating some random class parameterized by <T extends ISomeObject<T>>. And you can in theory say it like that: <T extends ISomeObject> but then you won't be able to use T in the "copyFrom" safely. In other words - it's a valid class parameterization, it has a point.
But then you can't parameterize it for ActualObject1:
SomeOtherParameterizedClass<ActualObject1> a1 = new SomeOtherParameterizedClass<>();
Yea - doesn't work. Hint:
SomeOtherParameterizedClass<ActualObject2> a2 = new SomeOtherParameterizedClass<>();
Works just fine...
So what's the right approach here? I'm more interested in retaining the type safety as much as possible, as for sure you can just use raw types all the way and don't worry about anything, but it's for the weak! :-)
We're in statically typed language so it's somewhat of an academic question - what's the right way of designing this class hierarchies with generics? Is occasionally using raw-types actually required for those to work?