Java generics <? extends Class> and Class<P> issue - java

I have the following peace of code:
public class LazyProductDataModel<P extends Product> extends LazyDataModel<P> {
private List<P> data = new ArrayList<P>();
private SearchProductsCriteria<P> criteria;
public LazyProductDataModel(SearchProductsCriteria<P> criteria) {
this.criteria = criteria;
}
public SearchProductsCriteria<P> getCriteria() {
return criteria;
}
public void setCriteria(SearchProductsCriteria<P> criteria) {
this.criteria = criteria;
}
#Override
public List<P> load(int first, int pageSize, String sortField, SortOrder sortOrder, Map<String, String> filters) {
if (criteria == null) {
return data;
}
int currentPage = first==0 ? 1 : first >= pageSize ? (first / pageSize) + 1 : 0;
criteria.setPageNumber(currentPage);
criteria.setPageSize(pageSize);
try {
if (criteria.getProductId() != null && criteria.getProductId() != 0) {
P product = ServiceClientFactory.getInstance().
getProductRetrieveClient(criteria.getProductClass()).getProduct(criteria.getProductId());
if (product != null) {
data.add(product);
this.setRowCount(1);
}
} else {
LazyDataResponse<P> lazyResponse = ServiceClientFactory.getInstance().
getProductSearchClient(criteria.getProductClass()).searchProductsLazyResponse(criteria);
data = lazyResponse.getList();
this.setRowCount(lazyResponse.getTotalRecordsCount());
}
} catch (Exception e) {
LOGGER.error("Exception happened at LazyProductDataModel#load.", e);
data = new ArrayList<P>();
this.setRowCount(0);
}
return data;
}
}
3rd line in the try block, getProductRetrieveClient(criteria.getProductClass()).getProduct(criteria.getProductId());
I am calling criteria.getProductClass(), this returns a Class<P>, and as you see in the class above, P extends Product, which means that I should be able to pass any class that extends Product, but in my class I am trying to use it but its not going as expected:
Here is how I am trying to use it, please help I'm new to the generics topic.
#ManagedBean
#ViewScoped
public class ReportController implements Serializable {
private ProductReportCriteria<? extends Product> criteria = new ProductReportCriteria<>();
public ReportController() {
}
#PostConstruct
public void init() {
criteria.setLocale(userToolKit.getLocale());
criteria.setProductClass(Movie.class);
}
public ProductReportCriteria<?> getCriteria() {
return criteria;
}
public void setCriteria(ProductReportCriteria<? extends Product> criteria) {
this.criteria = criteria;
}
}
in the last method onFilter inside the if statement, I am getting a compilation error,
The method setProductClass(Class<capture#9-of ? extends Product>) in the type SearchProductsCriteria<capture#9-of ? extends Product> is not applicable for the arguments (Class<Movie>)
public class Movie extends Product implements Serializable {
}
public class Product implements Serializable {
}
Finally,
public class ProductReportCriteria<P extends Product> extends SearchProductsCriteria<P> implements Serializable {
}
public class SearchProductsCriteria<P> implements Serializable {
private Class<P> productClass;
public Class<P> getProductClass() {
return productClass;
}
public void setProductClass(Class<P> productClass) {
this.productClass = productClass;
}
}

ProductReportCriteria<? extends Product> criteria doesn't mean anything that extends Product, it means a specific thing that extends Product, the compiler just doesn't know what.
A simpler example is List<? extends Number>: that doesn't mean "a list containing any subclass of Number": you can't add an Integer to such a list (list.add(Integer.valueOf(0))), even though Integer extends Number. This is because a List<Double> is a List<? extends Number>, and it would be problematic if you could add an Integer to a List<Double>:
List<Double> doubles = new ArrayList<>();
List<? extends Number> numbers = doubles; // Fine.
numbers.add(Integer.valueOf(0)); // Doesn't work; pretend it does.
Double d = doubles.get(0); // ClassCastException!
If you want criteria's consumer methods to accept any instance of Product, change its type to either of:
ProductReportCriteria<? super Product>
ProductReportCriteria<Product>
ProductReportCriteria<?> // Same as Product, because of declared bounds
Read up about PECS.

Related

Generics and factory method

I have two simple classes that extends an interface:
public interface Product
{
}
public class ProductA implements Product
{
}
public class ProductB implements Product
{
}
I have two 'services' classes: ServiceA and ServiceB. Each one works with one of the Product classes defined before. Both implements Service interface.
public interface Service<T extends Product>
{
public void print (T product);
}
public class ServiceA implements Service<ProductA>
{
public void print (ProductA product)
{
System.out.println("Product A");
}
}
public class ServiceB implements Service<ProductB>
{
public void print (ProductB product)
{
System.out.println("Product B");
}
}
I would like to developed a factory to instantiate my services:
[BEFORE I FOUND SOLUTION]
public class FactoryService
{
public static Service<? extends Product> getService (String serviceType)
{
Service<? extends Product> s = null;
if ("1".equals(serviceType))
s = new ServiceA();
else if ("2".equals(serviceType))
s = new ServiceB();
return s;
}
}
[SOLUTION]
public static <T> T getService (Type targetType)
{
T service = null;
if (!targetType.getClass().isInstance(Product.class))
throw new RuntimeException();
if (ProductA.class.getTypeName().equals(targetType.getTypeName()))
service = (T) new ServiceA();
else if (ProductB.class.getTypeName().equals(targetType.getTypeName()))
service = (T) new ServiceB();
return service;
}
When I tried to use my factory I get compile errors:
[BEFORE I FOUND SOLUTION]
public static void main(String[] args)
{
Product pA = new ProductA();
Product pB = new ProductB();
Service<? extends Product> service = FactoryService.getService("1");
service.print(pA);
}
[SOLUTION]
public static void main(String[] args)
{
Product pA = new ProductA();
Product pB = new ProductB();
Service<Product> service = FactoryService.getService(pA.getClass());
service.print(pA);
service = FactoryService.getService(pB.getClass());
service.print(pB);
// No compilation errors
}
The error says:
The method print(capture#5-of ? extends Product) in the type Service<capture#5-of ? extends Product> is not applicable for the arguments (Product).
How can I solve this problem?
Thanks
When you want to use generics in declaring a type, you shouldn't use <? extends Type> as Jesper pointed out. Instead you should use <Type>. In your case, replace Service<? extends Product> with Service<Product>.
Now you'll get another error:
Type mismatch: cannot convert from ServiceA to Service<Product>
The solution I suggest is instead of defining ServiceA and ServiceB, to just use the Service#print method and check the generic type, then do what is necessary. The FactoryService#getService method is not needed in this case.
Spoiler for my solution (try without it first):
public class Service<T extends Product> {
public void print(T product) {
if (product instanceof ProductA) {
System.out.println("Product A");
} else if (product instanceof ProductB) {
System.out.println("Product B");
}
}
}

Java generics - iterating through list of uninstantiated classes extending from same parent and call parent method

I have a list of classes with extend a base class
public class Entity{
abstract String getTitle();
}
The child classes are
public class ChildEntityOne extends Entity{
public static final String TITLE= "ABCD";
#Override
public String getTitle() {
return TITLE;;
}
}
public class ChildEntityTwo extends Entity{
public static final String TITLE= "EFGH";
#Override
public String getTitle() {
return TITLE;;
}
}
public class ChildEntityThree extends Entity{
public static final String TITLE= "WXYZ";
#Override
public String getTitle() {
return TITLE;;
}
}
now i'm trying to pass a list of valid classes to a function
which creates an instance from one of the classes from list and returns it
List<?ClassesToChooseFrom?> list = new ArrayList()<>;
list.add(?ChildEntityOne?);
list.add(?ChildEntityTwo?);
Entity result = getInstantiatedClass(list,getKey(),getjsonData())
if(result instanceof ChildEntityOne){
//do something
}else if(result instanceof ChildEntityTwo){
//do somwthing
}
public ?InstantiatedClassObject? getInstantiatedClass(List<?ClassesToChooseFrom?> list,String key,String jsonData){
foreach(?Class? itemclass : list){
if(itemClass.getTitle().equals(key)){
return new GsonBuilder().create().fromJson(jsonData, itemClass);}
}
return null;
}
Ive tried
List<Class<? extends Entity>> classes = new ArrayList<>();
but unable to go further..
You got the beginning right: a list of subclasses of Entity is:
List<Class<? extends Entity>> list = new ArrayList<>();
list.add(ChildEntityOne.class);
list.add(ChildEntityTwo.class);
Then you just need to make getInstantiatedClass use the same types you pass to it:
public Entity getInstantiatedClass(List<Class<? extends Entity>> list, String key, String jsonData) {
for (Class<? extends Entity> itemclass : list) {
if (getTitle(itemClass).equals(key)) {
...
You could make that generic, if you don't want to/need to do anything special for the Entity class.
public <T> T getInstantiatedClass(List<Class<? extends T>> list, String key, String jsonData) {
for (Class<? extends T> itemclass : list) {
...
To extract the value of the static TITLE field from a child entity class you can use:
private String getTitle(Class<?> itemclass) {
try {
return (String) itemclass.getField("TITLE").get(null);
} catch (IllegalAccessException | NoSuchFieldException e) {
return "N/A";
}
}

Java: Working with Generics and Maps without Casting / #SuppressWarnings

I now came several times across this problem and always kinda solved this with some casts and #SuppressWarnings annotations.
The relevant interfaces / abstract classes:
public abstract class Data { }
public interface DataOperations {
boolean isValid();
}
public interface DataOperationsFactory<T extends Data> {
Class<T> getDataClass();
DataOperations getOperations(T data);
}
Example Implementations:
public class DataImpl1 extends Data {
public String foo;
}
public class DataImpl1Operations implements DataOperations {
private DataImpl1 data;
public DataImpl1Operations(DataImpl1 data) {
this.data = data;
}
public boolean isValid() {
return data.foo != null;
}
}
public class DataImpl1OperationsFactory extends DataOperationsFactory<DataImpl1> {
public Class<DataImpl1> getDataClass() {
return DataImpl1.class;
}
DataOperations getOperations(DataImpl1 data) {
return new DataImpl1Operations(data);
}
}
Using this pattern, I can decide whether I need to create a new DataImpl1Operations everytime. Or maybe use a final static NO_OP implementation or what have you.
The Code:
Now I'd like to put all those factories inside a Map<Class<T>, DataOperationsFactory<T>> (constructor). And afterwards read from it (getOps method).
public class Test {
Map<Class<?>, DataOperationsFactory<?>> map;
public Test(List<DataOperationsFactory<?>> fs) {
for(DataOperationsFactory<?> f : fs) {
map.put(f.getDataClass(), f);
}
}
#SuppressWarnings("unchecked")
public <T extends Data> DataOperations getOps(T data) {
// --> Here I need to do an unchecked cast <--
DataOperationsFactory<? super T> f =
(DataOperationsFactory<? super T>) map.get(data.getClass());
return f.getOperations(data);
}
}
Is there any way doing this without unchecked casting?
You can delegate to a private method that captures the type, so it can be used to reliably cast to the correct Data subclass:
Map<Class<?>, DataOperationsFactory<?>> map;
// Unchanged
public Test(List<DataOperationsFactory<?>> fs) {
for(DataOperationsFactory<?> f : fs) {
map.put(f.getDataClass(), f);
}
}
public DataOperations getOps(Data data) {
DataOperationsFactory<?> f = map.get(data.getClass());
return getOperations(f, data);
}
private static <T extends Data> DataOperations getOperations(DataOperationsFactory<T> f,
Data data) {
return f.getOperations(f.getDataClass().cast(data));
}

How to instantiate this class parametrized by bonded interface?

I've got 3 interfaces:
public interface IAggregable<TElement extends IAggregable<TElement, TResult>, TResult> {
TResult aggregate(TResult intermediateResult);
}
public interface IDeeplyCloneable<TElement extends IDeeplyCloneable<TElement>> {
TElement deepClone();
}
public interface IContainer<TElement extends IAggregable<TElement, TResult> & IDeeplyCloneable<TElement>, TResult> {
TResult aggregateAllElements();
TElement cloneElementAtIndex(int index);
}
Then there are two classes that implement those 2 first interfaces:
public class Person implements IAggregable<Person, Integer>, IDeeplyCloneable<Person> {
private int age;
public Integer aggregate(Integer intermediateResult) {
if (intermediateResult == null) {
return age;
}
return age + intermediateResult;
}
public Person deepClone() {
Person clone = new Person();
clone.age = age;
return clone;
}
#Override
public String toString() {
return "Person [age=" + age + "]";
}
}
and almost exactly the same
public class Car implements IAggregable<Car, Integer>, IDeeplyCloneable<Car> {
private int horsepower;
public Integer aggregate(Integer intermediateResult) {
if (intermediateResult == null) {
return horsepower;
}
return horsepower + intermediateResult;
}
public Car deepClone() {
Car clone = new Car();
clone.horsepower = horsepower;
return clone;
}
#Override
public String toString() {
return "Car [horsepower=" + horsepower + "]";
}
}
then there's finally UselessContainer which suppose to implement IContainer and be able to hold both Person and Car and any other objects of any other class that implements IAggregable and IDeeplyCloneable.
public class UselessContainer<TElement extends IAggregable<TElement, TResult> & IDeeplyCloneable<TElement>, TResult> implements IContainer<TElement, TResult> {
private ArrayList<TElement> list;
public UselessContainer() {
this.list = new ArrayList<>();
}
public void add(TElement element) {
list.add(element);
}
#Override
public TResult aggregateAllElements() {
return null;
}
#Override
public TElement cloneElementAtIndex(int index) {
return null;
}
#Override
public String toString() {
return list.toString();
}
}
Question : How to create object of class UselessContainer?
I've tried that: UselessContainer<? extends IAggregable<?, Integer> & IDeeplyCloneable<?>, Integer> container;
You have marked your UselessContainer to be bound BOTH to IAggregable and IDeeplyCloneable. Please look at the & sign which indicates both of the interfaces must be implemented by business entity to be added to the list.
In order to use it in code - just create instance of UselessContainer without specyfing concrete generics types:
UselessContainer uc = new UselessContainer();
uc.add(new Person()); // works!
uc.add(new Integer(1)); // won't compile
uc.add(new Car()); // works!
I just tried it in my Editor and it works (JDK8).
EDIT:
You may create wrapping class:
class SimpleContainer {
public <TElement extends IAggregable<TElement, TResult> & IDeeplyCloneable<TElement>, TResult> void add(TElement el) {
UselessContainer<TElement, TResult> uc = new UselessContainer<>();
uc.add(el);
}
}
and use it:
SimpleContainer sc = new SimpleContainer();
sc.add(new Person());
You have 2 conditions you want to meet for UselessContainer<A, B> (well shorten this to U<A,B>):
(1) Cars can be added to U<A, B>
(2) Persons can be added to U<A, B>
Fulfilling both (1) and (2) is impossible, which can be proven using a indirect proof:
Assume fulfilling both conditions was possible.
The signature of add is add(TElement element) method and TElement extends IAggregable<TElement, TResult>.
From (1) and Car implements IAggregable<Car, Integer> we can therefore deduce A=Car.
And from (2) and Person implements IAggregable<Person, Integer> we can therefore deduce A=Person.
Combining those 2 results we get Car=Person which is obviously wrong. This contradiction concludes the proof.
This means you have to modify the restrictions on the type parameters or use the raw type.

Java Generics: Infer and reference a type used in the class instantiation

Given a KeyHolder interface such as the following:
public interface KeyHolder<K extends Key> {
K getKey();
}
I'd like to create a class like this:
public KeyHolderSet<H extends KeyHolder<K extends Key>> extends HashSet<H> {
public Set<K> getKeySet() {
Set<K> keySet = new HashSet<K>();
for (H keyHolder : this) {
keySet.add(keyHolder.getKey());
}
return keySet;
}
}
But that doesn't work, the closest I can get is this:
public KeyHolderSet<H extends KeyHolder<? extends Key>> extends HashSet<H> {
public <K extends Key> Set<K> getKeySet() {
Set<K> keySet = new HashSet<K>();
for (H keyHolder : this) {
// Explicit cast to K
keySet.add((K)keyHolder.getKey());
}
return keySet;
}
}
Any way around this?
Assuming the implementation class of the KeyHolder that is stored in the set is not important, you could try something like this:
public class KeyHolderSet<K extends Key> extends HashSet<KeyHolder<K>> {
public Set<K> getKeySet() {
...
}
}
You need to write it like this:
public KeyHolderSet<K extends Key, H extends KeyHolder<K>> extends HashSet<H> {
public Set<K> getKeySet() {
...
}
}
Unfortunately you will have to declare the type of K first and it cannot be inferred.
Maybe
public class KeyHolderSet<K extends Key, H extends KeyHolder<K>> extends
HashSet<H> {
public Set<K> getKeySet() {
...
}
}
if you don't mind parameterizing KeyHolderSet twice.
Given that KeyHolder<K extends Key> it seems unnecessary to use KeyHolderSet<H extends KeyHolder<K extends Key>> Just stick with:
public class KeyHolderSet<H extends KeyHolder<?>>
and you should be ok.
Edit: I see your problem with getKeySet(). This method should return H instead of K. H will be typed with what you entered in the declaration of KeyHolderSet (The variabe, not the class).
public class KeyHolderSet<H extends KeyHolder<?>> extends HashSet<H> {
private H theKeySet;
public void setKeySet(H keyset) {
theKeySet = keyset;
}
public H getKeySet() {
return theKeySet;
}
}
-
class betterKey extends Key {
}
-
public static void main(String[] args) {
KeyHolder<betterKey> kh = new KeyHolder<betterKey>() {
};
KeyHolderSet<KeyHolder<betterKey>> khs = new KeyHolderSet<KeyHolder<betterKey>>();
khs.setKeySet(kh);
KeyHolder<Key> kh2 = khs.getKeySet(); // Type mismatch
}
As you can see khs.getKeySet() returns KeyHolder<betterKey> as expected.
Edit2:
You can build up the set outside the KeyHolderSet class:
public static void main(String[] args) {
KeyHolderSet<KeyHolder<betterKey>> khs = new KeyHolderSet<KeyHolder<betterKey>>();
Set<betterKey> bks = new HashSet<betterKey>();
for (KeyHolder<betterKey> kh : khs) {
bks.add(kh.getKey());
}
}
One alternate solution i came up with is returning a completely generic Set, remember all type checking will be lost this way.
public class KeyHolderSet<H extends KeyHolder<?>> extends HashSet<H> {
public <K extends Key> Set<K> getKeySet() {
Set<K> s = new HashSet<K>();
for (H keyHolder : this) {
s.add((K) keyHolder.getKey()); // Unchecked cast
}
return s;
}
public static void main(String[] args) {
KeyHolderSet<KeyHolder<betterKey>> khs = new KeyHolderSet<KeyHolder<betterKey>>();
Set<Key> ks = khs.getKeySet(); // No problem
Set<betterKey> bks = khs.getKeySet(); // No problem
Set<evenBetterKey> ss = khs.getKeySet(); // No problem
}
}
class betterKey implements Key {
}
class evenBetterKey extends betterKey {
}

Categories

Resources