Generic class that extends collection generic class - java

I'm using Guava's LineProcessor interface when reading lines from files. I've created my class called Line Loader which will store lines read. I want it to be generic on choice of collection lines should be stored in so I wrote something like this:
public abstract class LineLoader<T> implements LineProcessor<Collection<T>> {
private final Collection<T> result;
public LineLoader() {
this.result = init();
}
protected boolean add(final T line) {
return result.add(line);
}
#Override
public boolean processLine(final String line) throws Exception {
final T data = parser.parseLine(line);
if (data == null) {
return false;
}
return add(data);
}
#Override
public Collection<T> getResult() {
return result;
}
protected abstract Collection<T> init();
}
where with init() method I force subclasses to chose type of collection, for example:
public abstract class LinkedLineLoader<T> extends LineLoader<T> {
#Override
protected Collection<T> init() {
return new LinkedList<T>();
}
}
I planned on doing this:
public class LineLoader<T> implements LineProcessor<C<T> extends Collection<T>> {
private final C<T> result;
public LineLoader() {
result = new C<T>();
}
protected boolean add(final T line) {
return result.add(line);
}
#Override
public boolean processLine(final String line) throws Exception {
final T data = parser.parseLine(line);
if (data == null) {
return false;
}
return add(data);
}
#Override
public C<T> getResult() {
return result;
}
}
so that latter subclases (if needed) could do:
public class LinkedLineLoader<T> extends LineLoader<LinkedList<T>> {
}
but it's not possible. Is there a clean solution to this problem?

In Java it is not possible to create instances from generic type arguments, because generics are erased at run-time. Also, generic type arguments can not themselves declare additional type arguments like C<T>. Therefore the code you posted is entirely illegal and will not compile:
private final C<T> result; // illegal
result = new C<T>(); // illegal
Aside from that, your declaration of the type arguments itself has some flaws. The following code is no legal Java code:
public class LineLoader<T> implements LineProcessor<C<T> extends Collection<T>>
It should actually look e.g. like this:
public class LineLoader<T, C extends Collection<T>> implements LineProcessor<C>
As a solution to your problem, you could declare your LineLoader as shown above, and add a protected constructor, that takes the generic collection as an argument:
public abstract class LineLoader<T, C extends Collection<T>> implements LineProcessor<C> {
private final C result;
protected LineLoader(C collection) {
result = collection;
}
protected boolean add(final T line) {
return result.add(line);
}
#Override
public boolean processLine(final String line) throws IOException {
final T data = parser.parseLine(line);
if (data == null) {
return false;
}
return add(data);
}
#Override
public C getResult() {
return result;
}
}
Then you can implement your LinkedLineLoader like so:
class LinkedLineLoader<T> extends LineLoader<T, LinkedList<T>> {
public LinkedLineLoader() {
super(new LinkedList<>());
}
}

Related

Why can't I use a class that implements List<X> where a List<X> is expected?

First the simple case (A):
public class PsList implements List<Ps> { ... }
elsewhere
private void doSomething(List<Ps> list) { ... }
// compiles
List<Ps> arrayList = new ArrayList<Ps>();
doSomething(arrayList);
// does not compile
PsList psList = new PsList();
doSomething(psList);
Ok. I know that I can change this to "work" by adding ? extends as such:
private void doSomething(? extends List<Ps> list) { ... }
// compiles
List<Ps> arrayList = new ArrayList<Ps>();
doSomething(arrayList);
// compiles
PsList psList = new PsList();
doSomething(psList);
My question is why do I need to do that? It makes no sense to me. I am implementing the exact interface that is is expecting. I can pass other List types other than ArrayList why not mine?
Life is always more complicated than this, so my real coding issue is (B):
public class PsList implements List<Ps> { ... }
private void doSomething(Map<String, ? extends List<Ps>> map, Boolean custom) {
...
// need to create a new List<Ps> of either an ArrayList<Ps> or PsList
map.put("stringValue", custom ? new PsList() : new ArrayList<Ps>());
...
}
So, in either case Java is complaining that map is expecting ? extends List as the value.
even if I change this to be:
List<Ps> list = new ArrayList<>();
List<Ps> psList = new PsList();
map.put("string", custom ? psList : list);
and of course this doesn't compile:
? extends List<Ps> list = new ArrayList<>();
? extends List<Ps> psList = new PsList();
map.put("string", custom ? psList : list);
So what am I supposed to do to get something like this to work?
Edit 1:
Ok, a minimal reproduction:
Ps.java
package com.foo;
public class Ps
{
}
PsList.java
package com.foo;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
public class PsList implements List<Ps>
{
#Override
public int size()
{
return 0;
}
#Override
public boolean isEmpty()
{
return false;
}
#Override
public boolean contains(Object o)
{
return false;
}
#Override
public Iterator<Ps> iterator()
{
return null;
}
#Override
public Object[] toArray()
{
return new Object[0];
}
#Override
public <T> T[] toArray(T[] a)
{
return null;
}
#Override
public boolean add(Ps ps)
{
return false;
}
#Override
public boolean remove(Object o)
{
return false;
}
#Override
public boolean containsAll(Collection<?> c)
{
return false;
}
#Override
public boolean addAll(Collection<? extends Ps> c)
{
return false;
}
#Override
public boolean addAll(int index, Collection<? extends Ps> c)
{
return false;
}
#Override
public boolean removeAll(Collection<?> c)
{
return false;
}
#Override
public boolean retainAll(Collection<?> c)
{
return false;
}
#Override
public void clear()
{
}
#Override
public Ps get(int index)
{
return null;
}
#Override
public Ps set(int index, Ps element)
{
return null;
}
#Override
public void add(int index, Ps element)
{
}
#Override
public Ps remove(int index)
{
return null;
}
#Override
public int indexOf(Object o)
{
return 0;
}
#Override
public int lastIndexOf(Object o)
{
return 0;
}
#Override
public ListIterator<Ps> listIterator()
{
return null;
}
#Override
public ListIterator<Ps> listIterator(int index)
{
return null;
}
#Override
public List<Ps> subList(int fromIndex, int toIndex)
{
return null;
}
}
OtherService.java
package com.foo;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class OtherService
{
private void doSomething(Map<String, List<Ps>> map, Boolean custom)
{
if (custom)
{
map.put("someValue", new PsList());
} else {
map.put("someValue", new ArrayList<>());
}
}
private void callDoSomethingNotCustom()
{
Map<String, List<Ps>> map = new HashMap<>();
doSomething(map, false);
}
private void callDoSomethingCustom()
{
Map<String, PsList> map = new HashMap<String, PsList>();
// map is not the right format
doSomething(map, true);
}
}
Wrong 1st argument type. Found: 'java.lang.String,com.foo.PsList>', required: 'java.util.Map>'
As you seemed to realize halfway through your question, your problem is not about List<Ps> being interchangeable with PsList.
The problem is that you can’t add to a Map<String, ? extends List<Ps>>.
Let’s consider a simpler example:
void doSomething(Map<String, ? extends Number> map) {
map.put(String, Integer.valueOf(0)); // Not allowed.
}
The problem is that Map<String, ? extends Number> does not mean “values can be Number or any subclass of Number.”
Every generically typed object has a specific, non-wildcard type. Meaning, there does not exist a Map whose type is Map<String, ? extends Number>. However, the following can exist:
Map<String, Integer> (allows Integer values only)
Map<String, Double> (allows Double values only)
Map<String, Number> (allows values of any Number subclass)
Map<String, ? extends Number> refers to a Map that might be any one of the above (or, of course, any other specific Number subclass). The compiler doesn’t know which specific type the Map’s values are, but the Map still has a specific type for its values which does not make use of ? in any way.
So, looking at the example method again:
void doSomething(Map<String, ? extends Number> map) {
// Not allowed. The caller might have passed a Map<String, Double>.
map.put(String, Integer.valueOf(0));
// Not allowed. The caller might have passed a Map<String, Integer>.
map.put(String, Double.valueOf(0));
// Not allowed. The caller might have passed a Map<String, Integer>
// or Map<String, Double>. This method has no way of knowing what the
// actual restriction is.
Number someNumber = generateNewNumber();
map.put(String, someNumber);
}
Indeed, you cannot add anything to a Map or Collection whose type is an upper bound wildcard, because there is no way to know whether it’s correct and safe to do so.
In your case, the simplest solution is to remove the wildcard:
private void doSomething(Map<String, List<Ps>> map, boolean custom) {
// ...
map.put("stringValue", custom ? new PsList() : new ArrayList<Ps>());
}
If you really have Maps with different value types, you would need to tell the method the specific type being used:
private <L extends List<Ps>> void doSomething(Map<String, L> map,
Supplier<L> listCreator) {
// ...
map.put("stringValue", listCreator.get());
}
And then you can call the method like this:
if (custom) {
doSomething(psMap, PsList::new);
} else {
doSomething(listMap, ArrayList::new);
}
You can absolutely do that. Here is an example:
public class MyClass {
static interface FooList<T> {}
static class Ps {}
static void doSomething(FooList<Ps> list) { }
static class PsList implements FooList<Ps> { }
public static void main(String[] args)
{
FooList psList = new PsList();
doSomething(psList);
System.out.println("HelloWorld!");
}
}
See demo here.

How can I add consumers of different arguments to a list?

I want to collect a list of Processors who can operate on different parameters.
Here are some example classes
public abstract class AbstractHistory {
public String getTlp() { return ""; }
}
public class SynchResourcConfig {
public boolean isEnabled() { return true); }
}
public class SynchClassTlpFilterConfig extends SynchResourcConfig {
public String getClass() { return ""; }
}
This is the interface I want each element of the list to implement
public interface ConfigProcessingFilter {
public boolean shouldProcess(AbstractHistory history, SynchResourceConfig config);
}
This class ideally would hold all the processors in a list
The idea being, when 'shouldProcess()' gets invoked, it can reply with the result of any included processor.
public class ConfigProcessor {
protected List<ConfigProcessingFilter> filters = new ArrayList<>();
public boolean shouldProcess(AbstractHistory history, SynchResourceConfig config) {
return config.isEnabled() && filters.stream().anyMatch(
(filter) -> filter.shouldProcess(history, config));
}
public ConfigProcessor addFilter(ConfigProcessingFilter filter) {
filters.add(filter);
return this;
}
}
The dilemma
I want to allow for processing on subclasses of the data in the list as well. But I'm getting the error indicated in the constructor's comments.
public class ClassTlpProcessingFilter extends ConfigProcessor {
public ClassTlpProcessingFilter() {
/*
Compiler underlines: `processByClassTlp()` with error:
The method processByClassTlp(AbstractHistory, SynchClassTlpFilterConfig)
in the type ClassTlpProcessingFilter is not applicable
for the arguments (AbstractHistory, SynchResourceConfig)
*/
addFilter((history, config) -> processByClassTlp(history, config));
}
public boolean processByClassTlp(AbstractHistory history, SynchClassTlpFilterConfig config) {
return config.getClass().equals(history.getTlp());
}
}
Looking at the issue
The List contains ConfigProcessingFilter. Each of those items has a shouldProcess() method.
I was hoping this would allow any subclass of the described types to be accepted as parameters.But obviously that isn't working.
Is there anything that could be done to fix this?
Thanks in advance
I think I figured out how to do it. It seems to work if I propagate the elements' type all the way up from the lowest child class up to the parent class.
Interface updated with type params
public interface ConfigProcessingFilter<H extends AbstractHistory, C extends SynchResourceConfig> {
public boolean shouldProcess(H history, C config);
}
Processor updated to operate on generic types
public class ConfigProcessor<H extends AbstractHistory, C extends SynchResourceConfig> implements ConfigProcessingFilter<H, C> {
private List<ConfigProcessingFilter<H, C>> filters = new ArrayList<>();
public boolean shouldProcess(H history, C config) {
return config.isEnabled() && filters.stream().anyMatch(
(filter) -> filter.shouldProcess(history, config));
}
}
public ConfigProcessor addFilter(ConfigProcessingFilter<H, C> filter) {
filters.add(filter);
return this;
}
}
Update to the subclass
public class ClassTlpProcessingFilter<H extends AbstractHistory, C extends SynchClassTlpFilterConfig> extends ConfigProcessor<H, C> {
public ClassTlpProcessingFilter() {
addFilter((history, config) -> processByClassTlp(history, config));
}
public boolean processByClassTlp(AbstractHistory history, SynchClassTlpFilterConfig config) {
return config.getClass().equals(history.getTlp());
}
}

How to initializing Class<T> at interface default method?

I have an interface:
public interface ITransformer<S,T>{
public void transform(S source,T target);
default String getTransformerName(){
Class<S> s;
Class<T> t;
return s.getName() + t.getName(); //*********
}
}
the error message the starred line:
The local variable s may not have been initialized
The local variable t may not have been initialized
I would like to use this method to return a string with [S.classname][T.classname] . Please let me know how to achieve this or is this impossible to do at interface ?
Update: Jan 12
My purpose of doing this is due to the fact that this class will be in framework and I want to reduce the human error as much as possible.. I am changing the code as follows:
public interface ITransformer<S,T>{
public void transform(S source,T target);
public FieldEntry<S, T> getTransformerName();
}
public class FieldEntry<S,T> implements Comparable<FieldEntry> {
private Class<S> s;
private Class<T> t;
public FieldEntry(Class<S> s,Class<T> t){
this.s = s;
this.t = t;
}
public String getEntryName(){
return s.getName() + t.getName();
}
#Override
public int compareTo(FieldEntry entry) {
if(entry == null) throw new IllegalArgumentException("The argument to compare cannot be null!");
return entry.getEntryName().compareTo(this.getEntryName());
}
}
In order to demonstrate why this can’t work, you may change your class to
public interface ITransformer<S,T>{
public void transform(S source,T target);
static <In,Out> ITransformer<In,Out> noOp() {
return (source,target) -> {};
}
static void main(String... arg) {
ITransformer<String,Integer> t1 = noOp();
ITransformer<Long,Thread> t2 = noOp();
System.out.println(t1 == (Object)t2);
}
}
Running this will print true. In other words, both functions are represented by the same instances, so there can’t be and property allowing to recognize their different type.
Generally, when two functions (lambda expressions or method references) exhibit the same behavior, a JVM may represent them by the same implementation type or even the same instance.
Even for non-interface classes, this doesn’t work due to Type Erasure. It only works when you have a reifiable (i.e. non-generic) type extending or implementing a generic type.
It's a little bit dangerous and I wouldn't used this in production (because you should cover in your code all possible use cases of your interface), but you can use reflection for it:
public interface ITransformer<S, T> {
public void transform(S source, T target);
default String getTransformerName() {
Type[] genericInterfaces = this.getClass().getGenericInterfaces();
ParameterizedType parameterizedType = null;
for (Type genericInterface : genericInterfaces) {
if (genericInterface instanceof ParameterizedType) {
ParameterizedType paramInterface = (ParameterizedType) genericInterface;
if (paramInterface.getRawType().equals(ITransformer.class)) {
parameterizedType = paramInterface;
break;
}
}
}
if (parameterizedType == null) {
throw new IllegalStateException("!");
}
return parameterizedType.getActualTypeArguments()[0].getTypeName() + parameterizedType.getActualTypeArguments()[1].getTypeName();
}
}
public class StringToIntegerTransfomer implements ITransformer<String, Integer> {
#Override
public void transform(String source, Integer target) {
}
}
public interface StringToNumberTransfomer<T extends Number> extends ITransformer<String, T> {
}
public class StringToLongTransfomer implements StringToNumberTransfomer<Long>, ITransformer<String, Long> {
#Override
public void transform(String source, Long target) {
}
}
#Test
public void test() {
ITransformer<String, Integer> intTransformer = new StringToIntegerTransfomer();
ITransformer<String, Long> longTransformer = new StringToLongTransfomer();
ITransformer<String, String> stringTransformer = new ITransformer<String, String>() {
#Override
public void transform(String source, String target) {
}
};
ITransformer<String, Double> doubleTransformer = new StringToNumberTransfomer<Double>() {
#Override
public void transform(String source, Double target) {
}
};
System.out.println(String.format("intTransformer: %s", intTransformer.getTransformerName()));
System.out.println(String.format("longTransformer: %s", longTransformer.getTransformerName()));
System.out.println(String.format("stringTransformer: %s", stringTransformer.getTransformerName()));
System.out.println(String.format("doubleTransformer: %s", doubleTransformer.getTransformerName()));
}
Output for this snippet:
intTransformer: java.lang.Stringjava.lang.Integer
longTransformer: java.lang.Stringjava.lang.Long
stringTransformer: java.lang.Stringjava.lang.String
java.lang.IllegalStateException: !
This code has one restriction, you should say implements ITransformer<S, T> for all implementations of ITransformer. That why I have got IllegalStateException for this line ITransformer<String, Double> doubleTransformer = new StringToNumberTransfomer<Double>(). But you can improve this code.
Better option is to use some base implementation of interface and pass source and target classes into constructor:
public interface ITransformer<S, T> {
void transform(S source, T target);
String getTransformerName();
}
public abstract class BaseITransformer<S, T> implements ITransformer<S, T> {
private final Class<S> sourceClass;
private final Class<T> targetClass;
public BaseITransformer(Class<S> sourceClass, Class<T> targetClass) {
this.sourceClass = sourceClass;
this.targetClass = targetClass;
}
public String getTransformerName() {
return sourceClass.getName() + targetClass.getName();
}
}
In Java it is impossible to get a Class<S>, unless you already know which class S is, or something else that knows which class S is gives you one.

Generify class because of unchecked casting warning

I have the following class with methods marked as unchecked that give me a warning. They compile/work fine but I am interested if there is a way to generify the class as IntelliJ IDEA is offering me (but unable to do) to remove the warnings.
public abstract class Attribute {
private final AttributedNode containerNode;
private boolean computationComplete;
public Attribute(AttributedNode node) {
this.containerNode = node;
this.computationComplete = false;
}
public AttributedNode getContainerNode() {
return containerNode;
}
public Attribute getAttributeFromChildNode(int childIndex, AttributeNameEnum attributeName) {
AttributedNode node = getContainerNode().getChild(childIndex);
return node.getAttribute(attributeName);
}
public String getChildNodeText(int index) {
return getContainerNode().getChild(index).getText();
}
#SuppressWarnings("unchecked")
public <T, E extends Attribute> List<T> getListOfChildAttributeValues(AttributeNameEnum name) {
List<T> result = new LinkedList<>();
for (AttributedNode node : this.getContainerNode().getChildren()) {
E attribute = (E) node.getAttribute(name);
result.add((T) attribute.getValue());
}
return result;
}
public <T extends Attribute> Object getAttributeValueFromContainer(AttributeNameEnum name) {
return this.<T>getAttributeValueFromNode(name, this.getContainerNode());
}
public <T extends Attribute> Object getAttributeValueFromParent(AttributeNameEnum name) {
return this.<T>getAttributeValueFromNode(name, this.getContainerNode().getParent());
}
#SuppressWarnings("unchecked")
public <T extends Attribute> Object getAttributeValueFromNode(AttributeNameEnum name, AttributedNode node) {
T attribute = (T) node.getAttribute(name);
return attribute.getValue();
}
// other methods eluded
P.S. I apologize ahead of time of maybe my lack of understanding Generics.

Can generics allow the Java compiler to check the type of keys and values in a map?

I am in a situation where I want to have a map where the keys are an interface class, and the corresponding value is a class which implements that interface. In other words the key and value type is related.
My current implementation of the method which adds to the map, and gets an instance of the implementation class looks like:
// should be something like Class<T>, Class<? extends T>
static Map<Class<?>, Class<?>> map = new HashMap<Class<?>, Class<?>> ();
public static <T> void add(Class<T> interfaceT,
Class<? extends T> implementationT) {
map.put(interfaceT, implementationT);
}
public static <T> T get(Class<T> interfaceT) {
// cast caused by definition not complete.
Class<T> implementationT = (Class<T>) map.get(interfaceT);
// try catch stuff omitted
T t = implementationT.newInstance();
return t;
}
My question is:
Can I define the "map" variable so the cast in the get(...) method is unneeded? I could not make the " new HashMap<Class<T>, Class<? extends T>>()' work, so either it is impossible or I missed something fundamental :)
Please advise :)
Edit: It turned out that the asSubclass() method on Class did what I wanted :D
Class<?> rawClassFromMap = map.get(interfaceT);
Class<? extends T> implementationT = rawClassFromMap.asSubclass(interfaceT);
It is fine that implementationT is of type "? extends T" as I just need a T object returned.
I like generics. Reminds me of Haskell...
It looks like the goal is something like the "Typesafe Heterogenous Container" described by Josh Bloch in Chapter 5 of Effective Java (item 29). In his case, he's mapping a type (Class<T>) to an (already-instantiated) instance (T).
You can do something similar, using asSubclass instead of cast:
final class Factory
{
private Map<Class<?>, Class<?>> map = new HashMap<Class<?>, Class<?>>();
<T> void map(Class<T> type, Class<? extends T> impl)
{
map.put(type, impl.asSubclass(type));
}
private <T> Class<? extends T> get(Class<T> type)
{
Class<?> impl = map.get(type);
if (impl == null)
throw new IllegalArgumentException("Unknown type: " + type);
return impl.asSubclass(type);
}
<T> T create(Class<T> type)
throws Exception
{
Class<? extends T> impl = get(type);
Constructor<? extends T> ctor = impl.getConstructor();
return ctor.newInstance();
}
}
I would suggest a Proxy. Here's the Java example.
public interface Bike {
public String getWheels();
public int getSize();
}
public class MountainBike implements Bike {
#Override
public int getSize() {
return 24;
}
#Override
public String getWheels() {
return "Treaded";
}
#Override
public String toString() {
String newLine = System.getProperty("line.separator");
StringBuilder sb = new StringBuilder();
sb.append("Type: MOUNTAIN").append(newLine);
sb.append("Wheels: ").append(getWheels()).append(newLine);
sb.append("Size: ").append(getSize()).append(newLine);
return sb.toString();
}
}
public class CruiserBike implements Bike {
#Override
public int getSize() {
return 26;
}
#Override
public String getWheels() {
return "Smooth";
}
#Override
public String toString() {
String newLine = System.getProperty("line.separator");
StringBuilder sb = new StringBuilder();
sb.append("Type: CRUISER").append(newLine);
sb.append("Wheels: ").append(getWheels()).append(newLine);
sb.append("Size: ").append(getSize()).append(newLine);
return sb.toString();
}
}
public class BikeProxy implements InvocationHandler {
private Object obj;
public static Object newInstance(Object obj)
{
return java.lang.reflect.Proxy.newProxyInstance(obj.getClass()
.getClassLoader(), obj.getClass().getInterfaces(),
new BikeProxy(obj));
}
public static <T> T newInstance(String className)
{
try
{
return (T) newInstance(Class.forName(className));
}
catch (ClassNotFoundException e)
{
throw new RuntimeException(e);
}
}
public static <T> T newInstance(Class<T> bikeClass)
{
try
{
return (T) java.lang.reflect.Proxy.newProxyInstance(Bike.class.getClassLoader(), new Class[]{Bike.class},
new BikeProxy(bikeClass.newInstance()));
}
catch (Exception e)
{
throw new RuntimeException(e);
}
}
private BikeProxy(Object obj)
{
this.obj = obj;
}
public Object invoke(Object proxy, Method m, Object[] args)
throws Throwable
{
Object result;
try
{
result = m.invoke(obj, args);
}
catch (InvocationTargetException e)
{
throw e.getTargetException();
}
catch (Exception e)
{
throw new RuntimeException(e);
}
return result;
}
}
public class ProxyTester
{
public static void main(String[] args)
{
Bike mountainBike = BikeProxy.newInstance(MountainBike.class);
System.out.println(mountainBike);
Bike mountainBike2 = BikeProxy.newInstance(MountainBike.class.getName());
System.out.println(mountainBike2);
Bike cruiserBike = BikeProxy.newInstance(CruiserBike.class);
System.out.println(cruiserBike);
Bike cruiserBike2 = BikeProxy.newInstance(CruiserBike.class.getName());
System.out.println(cruiserBike2);
}
}

Categories