Generics and factory method - java

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");
}
}
}

Related

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

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.

Unchecked call and Unchecked assignment java factory generics

Consider the following scenario:
public abstract class A {}
public class B extends A {}
public interface Provider<T extends A> {
List<String> list(T param);
}
public class ProviderB implements Provider<B> {
#Override
public List<String> list(B param) {
return Collections.singletonList("ProviderB");
}
}
public class Factory {
public static Provider get(int x) {
if (x == 1)
return new ProviderB();
throw new RuntimeException("Not supported");
}
}
public class Main {
public static void main(String[] args) {
Provider provider = Factory.get(1);
A a = new B();
List<String> result = provider.list(a);
}
}
In Main at List<String> result = provider.list(a); , I'm getting:
Unchecked call to list(T) ..
Unchecked assignment java.util.List .. Reason 'provider' has raw type.
I do know some basic stuff about type erasure in generics. How would you solve the warnings ?
EDIT:
Actually main will look like this:
public static void main(Map<Integer, ? extends A> types) {
for (Map.Entry<Integer, ? extends A> entryType : types.entrySet()) {
Provider provider = Factory.get(entryType.getKey());
List<String> result = provider.list(entryType.getValue());
}
}

How to deliver the class of a generic type to a method in Java?

I want to implement a class that instantiates generic types.
public class DisjointSet<T extends Set<E>, E> {
private final Class<T> setType;
public DisjointSet(Class<T> setClass) {
this.setType = setClass;
}
public void doSomething(E Element) {
T set = setClass.newInstance();
set.add(element);
}
}
I tried instantiating the class like this:
DisjointSet<HashSet<Integer>, Integer> disjointSet = new DisjointSet<>(HashSet<Integer>.class);
However using .class on a generic type does not seem to be allowed. How would I correctly pass the required Class of a generic type to the constructor?
Not sure it is good to expose the inner set type (Hash versus other) in the parameterized type.
Actually due to type erasure you can't instantiate parameterised types directly, but you can pass in a factory,
package langGenerics;
import java.util.HashSet;
import java.util.Set;
public class UseGenerics {
public static void main(String[] args) {
SetFactory<Integer> setFactory = HashSet::new;
DisjointSet<Integer> disjointSet = new DisjointSet<>(setFactory);
disjointSet.doSomething( 123 );
}
}
interface SetFactory<T> { Set<T> get(); }
class DisjointSet<T> {
private SetFactory<T> setFactory;
public DisjointSet(SetFactory<T> setFactory) {
this.setFactory = setFactory;
}
public void doSomething(T item) {
Set<T> set = setFactory.get();
set.add(item);
}
}
If you really want to init your own set storage, then I suggest you to pass Supplier to your constructor:
public static class DisjointSet<T extends Set<E>, E> {
T set;
public DisjointSet(Supplier<T> supplier) {
set = supplier.get();
}
public void doSomething(E element) {
set.add(element);
}
}
Then use it:
DisjointSet<HashSet<Integer>, Integer> set = new DisjointSet<>(HashSet::new);
if this is what you wanted,
public class DisjointSet<T extends Set<E>, E> {
private final Class<T> setType;
public DisjointSet(Class<T> setClass) {
this.setType = setClass;
}
public static void main(String[] args) {
DisjointSet<HashSet<Integer>, Integer> disjointSet = new DisjointSet(new HashSet<Integer>().getClass());
}
}

Use Generics <K> to reduce duplicate code?

I have below code. As you can see I am executing similar logic but once for Bike and once for Car. Can I make use for <K> to reduce duplicate looking code? I have not used <K> so I am not sure where and how exactly I can incorporate it. Where can I make the decision whether to call getCarsWithFeature or getBikesWithFeature?
Is it best practice to reduce number of lines (may make it less readable) or to have such duplicate-looking code?
public Set<Car> getPremiumCars(String filter) {
final Callable<Set<Car>> retryGetCars = new RetryingCallable<>(retryStrategy(), getCars(filter));
return retryGetCars.call();
}
public Callable<Set<Car>> getCars(String feature) {
return new Callable<Set<Car>>() {
#Override
public Set<Car> call() throws Exception {
Set<Car> cars = getCarsWithFeature(feature);
return Collections.unmodifiableSet(cars);
}
};
}
public Set<Bike> getPremiumBikes(String filter) {
final Callable<Set<Bike>> retryGetBikes = new RetryingCallable<>(retryStrategy(), getBikes(filter));
return retryGetBikes.call();
}
public Callable<Set<Bike>> getBikes(String feature) {
return new Callable<Set<Bike>>() {
#Override
public Set<Bike> call() throws Exception {
Set<Bike> bikes = getBikesWithFeature(feature);
return Collections.unmodifiableSet(bikes);
}
};
}
I do not know your whole code, but I would suggest for both classes to implement same interface - let's say Vehicle:
public interface Vehicle {
}
Then you could write code that lately you can reuse:
public <T extends Vehicle> Set<T> getPremiumVehicle(Function<String, Callable<Set<T>>> vehicleSupplier, String filter) throws Exception {
final Callable<Set<T>> retryGetCars = new RetryingCallable<T>(retryStrategy(), vehicleSupplier.apply(filter));
return retryGetCars.call();
}
public <T extends Vehicle> Callable<Set<T>> getVehicle(Function<String, Set<T>> vehicleSupplier, String feature) {
return () -> {
Set<T> vehicles = vehicleSupplier.apply(feature);
return Collections.unmodifiableSet(vehicles);
};
}
Now, you could reuse above code, like:
public Set<Car> getPremiumCars(String filter) throws Exception {
return getPremiumVehicle(this::getCars, filter);
}
public Set<Bike> getPremiumBikes(String filter) throws Exception {
return getPremiumVehicle(this::getBikes, filter);
}
public Callable<Set<Car>> getCars(String feature) {
return getVehicle(this::getCarsWithFeature, feature);
}
public Callable<Set<Bike>> getBikes(String feature) {
return getVehicle(this::getBikesWithFeature, feature);
}
Create a base class of Car and Bike, then put the common method there.
Then extend the Car and Bike from it. Update the common method using the base class. Sample hints for the implementation is given below:
class Vehicle {
public Set<Vehicle> getWithFilter(String filter) {
final Callable<Set<Vehicle>> retryGet = new RetryingCallable<>(retryStrategy(), get(filter));
return retryGet.call();
}
public Callable<Set<Vehicle>> getWithFeature(String feature) {
return new Callable<Set<Vehicle>>() {
public Set<Vehicle> call() throws Exception {
Set<Vehicle> vehicles = getWithFeature(feature);
return Collections.unmodifiableSet(vehicles);
}
};
}
}
class Car extends Vehicle {
}
class Bike extends Vehicle {
}
Now to call use:
Car car = new Car();
car.getWithFilter(/* Pass parameter*/);
Bike bike = new Bike();
bike.getWithFilter(/* Pass parameter*/);

data access object pattern with abstract factories, creation over generic types

I'm developing a database application for android devices.
First thing I need to do is creating the data access layer.
For this I want to use DAO-Pattern with abstract factories.
For all DAOs i have one Interface witch contains the declaration that all data object needs to implement. (in my case: IDataObject)
The specific DAOs are all represented by its own interface, extending the base interface of all DAOs.
base interface:
public interface IDataObject {
public IDataId getId();
public void write() throws MyDataWriteException;
public void validate() throws MyDataValidException;
}
a extensions:
public interface IDataSample1 extends IDataObject {
public void setNotice(String notice);
public String getNotice();
public void setDate(Date date);
public Date getDate();
}
To create an data object I want use abstract to use abstract factories, something like:
public interface IDataFactory<Template extends IDataObject> {
public List<Template> getAll();
public Template get(IDataId id);
public List<Template> getList(DataAccessArgument arg);
public List<Template> getList(List<DataAccessArgument> argList);
}
and the implementation:
public class DataSample1Fac implements IDataFactory<IDataSample1> {
public DataSample1Fac () {
}
public List<IDataSample1> getAll() {
return null;
}
public IDataSample1 get(IDataId id) {
return null;
}
public List<IDataSample1> getList(DataAccessArgument arg) {
return null;
}
public List<IDataSample1> getList(List<DataAccessArgument> argList) {
return null;
}
}
I don't get any error so far, but now I want to implement an factory builder:
public class DataFactoryBuilder {
private DataFactoryBuilder() {
}
public static<T extends IDataObject> IDataFactory<T> getFactory(){
if (T instanceof IDataSample1)
return new DataSample1Fac();
return null;
}
}
I get following errors(line 8):
T cannot be resolved to a variable
and (line 9)
Type mismatch: cannot convert from DataSample1Fac to IDataFactory<T>
Don't know how to fix this, any suggestions?
I would refactor Your's DataFactoryBuilder to something like that:
class DataFactoryBuilder {
private DataFactoryBuilder() {
}
public static IDataFactory<? extends IDataObject> getFactory(Class<? extends IDataObject> clazz){
if (IDataSample1.class.isAssignableFrom(clazz)) {
return new DataSample1Fac();
}
return null;
}
}
I got following solution:
public static <T extends IDataObject> IDataFactory<T> getFactory(Class<T> type) {
if (IDataSample1.class.isAssignableFrom(type)) {
DataSample1Facfac = new DataSample1Fac();
return (IDataFactory<T>) fac;
}
}
but i get an warning on: return (IDataFactory) fac;
Type safety: Unchecked cast from DataSample1Fac to IDataFactory<T>
I think that is not a problem, I just have to supress it

Categories

Resources