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.
Related
I would like to create a contract (an interface), with a generic parameter, which enforces that the implemented class must also be the type specified in the generic parameter.
public interface SelfDefaultAlternativeDetailSupport<T extends AlternativeDetail> extends T { // syntax error (extends T)
default T resolveDetail() {
if (someConditions()) {
return this;
} else {
return getAlternativeDetails().stream()
.filter(somePredicate)
.findFirst().orElse(null);
}
}
List<T> getAlternativeDetails();
}
Example Usage
public interface CustomerDetail extends AlternativeDetail {
String getName();
}
public class Customer implements SelfDefaultAlternativeDetailSupport<CustomerDetail>, CustomerDetail {
#Override
public String getName() {
return "default name";
}
#Override
public List<AlternativeDetails> getAlternativeDetails() {
...
}
}
In other words, I would like that when a class implements SomeInterface<X>, the class must also implement X, But the attempt above has syntax because I cannot make SelfDefaultAlternativeDetailSupport extends T. Is this possible in Java?
You can make it a self-referential generic type:
public interface SelfDefaultAlternativeDetailSupport<T extends SelfDefaultAlternativeDetailSupport<T> & AlternativeDetail> {
#SuppressWarnings("unchecked")
default T resolveDetail() {
if (someConditions()) {
return (T) this;
} else {
return getAlternativeDetails().stream()
.filter(somePredicate)
.findFirst().orElse(null);
}
}
List<T> getAlternativeDetails();
}
Then you can have:
public class Customer implements SelfDefaultAlternativeDetailSupport<Customer>, CustomerDetail {
#Override
public List<Customer> getAlternativeDetails() {
...
}
}
Just be careful to never use a different class as the parameter to SelfDefaultAlternativeDetailSupport. If you want to avoid the unchecked cast of this, you can use getThis methods, but I don't think it's really worth it.
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.
Please consider the following code:
public abstract class Subject {
private Collection<Observer> observerCollection = new HashSet<>();
// ...
protected void notifyObservers() {
this.observerCollection.stream().filter(Objects::nonNull).forEach(o -> o.update(this));
}
}
public interface Observer<T extends Subject> {
void update(T subject);
}
I am getting the following compile-time warnings:
Observer is a raw type. References to generic type Observer should be parameterized
Type safety: The method update(Subject) belongs to the raw type Observer. References to generic type Observer should be parameterized
One comes at the call to update and for the life of me I can't figure out how to resolve it without using the warning suppressions. I've tried several ways to resolve the warning without luck. Any ideas on how this can be resolved?
Motivation
Consider the following client code:
public class IntegerContainer extends Subject {
private int integer;
public IntegerContainer(int integer) {
this.integer = integer;
}
public int getInteger() {
return this.integer;
} // ...
}
public class IntegerObserver implements Observer<IntegerContainer> {
private int cachedInteger;
#Override
public void update(IntegerContainer subject) {
this.cachedInteger = subject.getInteger(); // avoid cast here.
} // ...
}
The motivation for using generics in the Observer is to avoid a cast of the subject parameter so that the observer can retrieve the state of the subject.
This doesn't have anything to do with streams; it just straight up won't work.
An Observer<? extends Subject> is more or less unusable, because you don't know what subtype of Subject it's an observer of. For all you know, observerCollection only contains an Observer<SomeSubtypeOfSubjectThatNobodyEvenHeardOf>. (See the PECS principle; Observer is a consumer.)
I don't think there's any type-safe way to do this cleanly, frankly, because you can't say in Subject that the attached observers all accept this subtype of Subject, because there's no way to refer to "this subtype of Subject." The closest hack I can think of is
abstract class Subject<T extends Subject<T>> {
private Collection<Observer<? super T>> observers;
protected void notifyObservers() {
this.observerCollection.stream().filter(Objects::nonNull).forEach(o -> o.update((T) this)); // yes, this cast is unchecked
}
}
class SubSubject extends Subject<SubSubject> {
...
}
I'd focus on the value being passed between the Subject and Observer. I.e. both classes have one type parameter and the related methods make sure that the types are compatible:
public interface Observer<T> {
void update(T value); // no subject, but a value
}
public class Subject<T> {
private Collection<Observer<? super T>> observers = new HashSet<>();
protected void notifyObservers() {
this.observers.stream().filter(Objects::nonNull).forEach(o -> o.update(this.getValue()));
}
public void addObserver(Observer<T> observer) { // adding the right kind of observer
observers.add(observer);
}
abstract public T getValue(); // returning the value - this one is abstract
}
The key above is the abstract public T getValue(); method. Here is how you can write an IntegerContainer and and IntegerObserver :
public class IntegerContainer extends Subject<Integer> {
private int value;
public IntegerContainer(int value) {
this.value = value;
}
#Override
public Integer getValue() {
return value; // this is the parameter of the update() call
// you could even compute here something
// you can pass entire objects too, if required
}
}
public class IntegerObserver implements Observer<Integer> {
private int cachedInteger;
#Override
public void update(Integer value) {
this.cachedInteger = value; // no cast here
} // ...
}
You can put them together like this:
IntegerContainer container = new IntegerContainer(3);
IntegerObserver observer = new IntegerObserver();
container.addObserver(observer);
container.notifyObservers();
I need to provide a generic interface to be used in separate class hierarchies and would like that interface to support invocation chaining.
I have tried to do this using reflective bounds, but I can't seem to get it to work without having to cast "this" to the desired type. Here is my current approach:
public interface Operable<T extends Operable<T>> {
T prepare();
T operate();
}
public abstract class BaseOperable<T extends Operable<T>> implements Operable<T> {
#Override
public T prepare() {
System.out.println("Preparing...");
// why is this needed? "this" is a BaseOperable that implements Operable<T>
return (T) this;
}
}
public class SpecialOperable<T extends SpecialOperable<T>> extends
BaseOperable<SpecialOperable<T>> {
#Override
public T operate() {
System.out.println("Operation "
+ (Math.random() > 0.5 ? "succeeded" : "failed"));
// this seems to be required
return (T) this;
}
#Override
public T prepare() {
// if I don't override this, the supertype T is used which is
// BaseOperable and hides "specialOp" from chaining
return (T) super.prepare();
}
public T specialOp() {
System.out.println("Doing something special...");
return (T) this;
}
}
The above the following line of code to compile:
mySpecialOperable().prepare().operate().specialOp().operate();
My question is: is there any way to avoid type-casting of every return statement? Is it also possible to not have to override everything at the most specialized level (as is done with the prepare() method)?
The problem is that you are assuming type safety at a place where the compiler cannot determine at compile-time if a casting is legal. This entails the warning you encounter: For making this more clear, assume:
class Foo implements Operable<Foo> { ... } // legal
class Bar implements Operable<Foo> { ... } // not intended, but also legal
The Bar class is not intended to be legal by your abstraction model. You want to implement a self-type but all you can factually require in Java is to demand the extension of a type T that implements a given interface. Thus, extending
class Bar extends BaseOperable<Foo> { ... }
would basically be executed at run time as if you implemented:
class Bar implements Operable<Foo> {
#Override
public Foo prepare() {
System.out.println("Preparing...");
// Why is this needed, you ask? Because you are now expressing this:
return (Foo) this; // this is of type Bar, not Foo
}
...
}
where this is however an instance of Bar but not of Foo. This, of course, causes a ClassCastException and for this possibility, the compiler warns you that static type-safety has failed you and this exception might happen where you would not normally expect it.
You normally avoid this by adding a method like:
protected abstract T self();
which is then implemented by any non-abstract class. As this method is then implemented with a non-generic return type, the static compile check can do its work and forbid you any illegal return types as we observed before. The above prepare method would then be implemented like this:
public T prepare() {
System.out.println("Preparing...");
return self();
}
If you are however working with a closed API that is not implemented by any user and you want to take that short cut (hopefully, you have unit tests that validate against any possible abuse), you can annotate the method with #SupressWarnings("unchecked") to tell the compiler that you are aware of what you are dealing with.
Your problem can be simplified to:
public abstract class AbstractOperable<T extends AbstractOperable<T>> {
public T prepare() {
// Type mismatch: cannot convert from AbstractOperable<T> to T
return this;
}
}
public class OperableImpl extends AbstractOperable<OperableImpl> {
}
Now, consider the following class:
public class OperableImplHack extends AbstractOperable<OperableImpl> {
}
While it fulfills the T extends AbstractOperable<T> contract, this is not T. This is the reason why the compiler can't know if this is T or not in AbstractOperable.
Solution #1:
public abstract class AbstractOperable<T extends AbstractOperable<T>> {
public abstract T getThis();
public T prepare() {
return getThis();
}
}
public class OperableImpl extends AbstractOperable<OperableImpl> {
#Override
public OperableImpl getThis() {
return this;
}
}
Solution #2:
public abstract class AbstractOperable<T extends AbstractOperable<T>> {
protected T that;
public T prepare() {
return that;
}
}
public class OperableImpl extends AbstractOperable<OperableImpl> {
public OperableImpl() {
that = this;
}
}
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.