Java generic List with genric items as parameter - java

I have the following interface with generic and an implentation class:
public interface DataInterface<T> {
T getData();
void printData();
}
public class IntegerData implements DataInterface<Integer> {
private Integer value;
public IntegerData(Integer value) {
this.value = value;
}
#Override
public Integer getData() {
return null;
}
#Override
public void printData() {
System.out.println(this.value);
}
}
And here my code that use the class:
public class Main {
public static void main(String[] args) {
List<IntegerData> dataList = new ArrayList<>();
dataList.add(new IntegerData(1));
doSomething(dataList); <-- Compiler error
//this work
doSomething(Collections.unmodifiableList(dataList));
doSomething(new ArrayList<>(dataList));
}
private static void doSomething(List<DataInterface<?>> dataList) {
for (DataInterface<?> data : dataList)
data.printData();
}
}
If I try to call the doSomething method with the List type, the compiler complains with "The method doSomething(List<DataInterface<?>>) in the type Main is not applicable for the arguments (List)".
But if I wrap my specific list or create a new one, then it works. I wonder why the direct call doesn't work. What is the reason?

A List<IntegerData> or List<DataInterface<Integer>> is not compatible with List<DataInterface<?>>, because I can also add a DataInterface<String> to the latter. The solution is to use extends:
private static void doSomething(List<? extends DataInterface<?>> dataList)
This will prevent adding anything to the list (except null), and is therefore safe to use.
By wrapping the list, the generic type is changed due to type inference. If you'd assign it to a variable using var it would also fail.

Related

Java Stream to Array after mapping

I have the following Code which is a boiled-down version of something I've stumbled upon:
public class Transforming
{
static interface MyInterface<T>
{
void consume(T... toConsume);
}
static abstract class Mapper<T> implements MyInterface<String> {
MyInterface<T> delegate;
public Mapper(MyInterface<T> delegateTo)
{
delegate = delegateTo;
}
public void consume(String... transformFrom)
{
T[] array = (T[]) Arrays.stream(transformFrom)
.map(this::transform)
.toArray(); // can't toArray(T[]::new) here!
delegate.consume(array);
}
protected abstract T transform(String toTransform);
}
}
The searches on how to transform streams to arrays fall short obviously since I don't have the resulting type of array at this point, and Java doesn't allow me to create arrays of a generic type...
I do understand the issue, but any input on how to clean code this?
AFAICT, my options here are
change the interface from varargs to List
the cast I'm using in the code sample
adding an IntFunction to the Mapper creation
Anything I'm missing?
What would be your preference?
The way I handle this is by always providing two overloads:
One which accepts varargs
One which accepts a List<>.
The varargs overload never does anything other than pack the array into a list and invoke the List<> overload. This keeps things simple. No-brainer.
So, essentially, the option I'd choose is your first option, "change the interface from varargs to List", except that you do not actually have to change it, you can just extend it by adding an overload.
Your abstract Mapper class could use an abstract toArray method which provide the typed conversion from list to array.
static abstract class Mapper<T> implements MyInterface<String> {
#Override
public void consume(String... transformFrom) {
T[] array = toArray(Arrays.stream(transformFrom)
.map(this::transform)
.collect(Collectors.toList()));
delegate.consume(array);
}
protected abstract T transform(String toTransform);
protected abstract T[] toArray(List<T> list);
}
In implementations just implement a basic list.toArray(..) method
public static void main(String[] args) {
Mapper myMap = new Mapper<Integer>(new MapperInt()) {
#Override
protected Integer transform(String toTransform) {
return new Integer(toTransform);
}
#Override
protected Integer[] toArray(List<Integer> list) {
return list.toArray(new Integer[list.size()]);
}
};
myMap.consume("1","2");
}
public static class MapperInt implements MyInterface<Integer> {
#Override
public void consume(Integer... toConsume) {
for(Integer i: toConsume)
System.err.println(i);
}
}

Can't resolve this Java generics compile-time warning

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();

Parameterized classes and methods: making a method returns a correct type with java generics

I have a class AbstractExtractionRules which constructor receives a ParserAPI object. The AbstractExtractionRules will be implemented using many different Parser APIs and each uses its own abstraction of 'Document'.
ParserAPI class has a parameterized type that represents the return type for the method parseDocument.
I want a way to use the ParserAPI in AbstractExtractionRules subclasses without the need of cast, leaving it in a more natural way.
I think with java generics, perhaps modifying the constructor parameter accordingly or modifying the call for getParserAPI().parseDocument(htmlCode) I can reach this, but I do not know how to do.
#FunctionalInterface
public interface ExtractionRules<T> {
List<T> extract(String htmlCode);
}
public interface ParserAPI<T> {
T parseDocument(String htmlCode);
}
public abstract class AbstractExtractionRules <T> implements ExtractionRules <T> {
private ParserAPI<?> parserAPI;
public AbstractExtractionRules(ParserAPI<?> parserAPI) {
this.parserAPI = parserAPI;
}
public ParserAPI<?> getParserAPI() {
return parserAPI;
}
}
public class RibeiraoVisitorRule extends AbstractExtractionRules <String> {
public RibeiraoVisitorRule(ParserAPI<Document> parserAPI) {
super(parserAPI);
}
#Override
public List extract(String htmlCode) {
List<String> list = new ArrayList<>();
Document doc = (Document) getParserAPI().parseDocument(htmlCode);
Elements submenu = doc.select("a.new_sub_menu");
submenu.forEach(element1 -> {
String href = element1.attr("abs:href");
list.add(href.concat("&pageNum=VER-TUDO"));
});
return list;
}
}
You can pass type from AbstractExtractionRules to ParserAPI:
public abstract class AbstractExtractionRules<A, T> implements ExtractionRules<T> {
private ParserAPI<A> parserAPI;
Then you can call it without cast in concrete implementation class:
public class RibeiraoVisitorRule extends AbstractExtractionRules<Document, String> {
#Override
public List<String> extract(String htmlCode) {
...
Document doc = getParserAPI().parseDocument("");
}
Note that I also added passing type T to ExtractionRules interface. It affects the return type of extract() method. In your example you did not pass the type so return type of the method was List.

Need help in understanding java generics concept

I am trying to find the bug in the following code which I purposely screwed only to explore concept in depth.
import java.util.ArrayList;
import java.util.Iterator;
class typeAnimal<String> implements Iterable<String> //// (1)
{
ArrayList<String> listAnimal = new ArrayList<String>();
typeAnimal(ArrayList<String> listAnimal)
{
this.listAnimal = listAnimal;
}
#Override
public Iterator<String> iterator()
{
return new Iterator<String>()
{
int position;
#Override
public boolean hasNext() {
if(position<listAnimal.size())
return true;
else
return false;
}
#Override
public String next()
{
String aniPos = listAnimal.get(position);
position ++;
return aniPos;
}
#Override
public void remove() {
// TODO Auto-generated method stub
}
};
}
}
public class newIterator
{
public static void main(String[] args)
{
ArrayList<String> animalList = new ArrayList<String>();
animalList.add("priyank");
animalList.add("kannu");
typeAnimal<Object> animalName= new typeAnimal(animalList);
for(String name:animalName) //// <-- Error
{
System.out.println(name);
}
}
}
The error is Type mismatch: cannot convert from element type Object to String. But if I replace typeAnimal<String> in (1) with typeAnimal<Object> the error will go away.
I know this is worth less code but still wish to know what exactly causing error.
class typeAnimal<String> implements Iterable<String>
When a class has a generic parameter, it is usually called T or other single letter names. This makes it clear that actual instances of this class must replace that parameter with a real class.
When you call your type parameter String, you confuse the compiler, since there is already a String class in Java.
You should either define a non generic class :
class typeAnimal implements Iterable<String>
or a generic class :
class typeAnimal<T> implements Iterable<T>
The former means that your class implements an Iterable over the String class.
The latter means your class implements an Iterable over a generic type parameter.

Java Generics and unchecked cast

I'm struggling with this aspect of Generics in Java. Hopefully someone can help me see the ways.
I have a class that holds a List of objects. This code works, but I want to get rid of the cast. How can I make this more generic?
public class Executor {
List<BaseRequest<BaseObj>> mRequests = new ArrayList<BaseRequest<BaseObj>>();
public Executor() {
}
#SuppressWarnings("unchecked")
public <T extends BaseObj> void add(final BaseRequest<T> request) {
mRequests.add((BaseRequest<BaseObj>) request);
}
public void execute() {
for (BaseRequest<BaseObj> r : mRequests) {
// DO SOMETHING WITH r
}
}
}
In the posted snippet you need the cast because BaseRequest<? extends BaseObj> is not a subtype of BaseRequest<BaseObj>, and the cast can't be checked at runtime because of type erasure, and that's why the compiler warns you. But if you change the declaration of mRequests:
public class Executor {
List<BaseRequest<? extends BaseObj>> mRequests = new ArrayList<>();
public Executor() {
}
public <T extends BaseObj> void add(final BaseRequest<T> request) {
mRequests.add(request);
}
public void execute() {
for (BaseRequest<? extends BaseObj> r : mRequests) {
// DO SOMETHING WITH r
}
}
}
class BaseRequest<T> {}
class BaseObj {}
Let's resolve the problem step-by-step. You want to be able to call
req.add(new BaseRequest<ExtObj1>());
req.add(new BaseRequest<ExtObj2>());
req.add(new BaseRequest<ExtObj3>());
where ExtObj[1|2|3] extends BaseObj. Given the List interface:
List<T> {
void add(T el);
}
we need to find a common supertype for BaseRequest<ExtObj1>, BaseRequest<ExtObj2> and BaseRequest<ExtObj3>. One supertype is BaseRequest<?> and another one is BaseRequest<? extends BaseObj>. I picked the second one because it's the most restrictive possible. You should know that in Java BaseRequest<ExtObj1> is not a subtype of BaseRequest<BaseObj> because generics are invariant.
Now that we have the right declaration for mRequests, finding the API for Executor.add() is straightforward. BTW, if the method body you need is really that simple, you don't even need the type parameter:
public void add(BaseRequest<? extends BaseObj> request) {
mRequests.add(request);
}
Warnings are not errors. Warnings are there so you check if you have an error because it may not be checked automatically. You should check it and then use the annotation to note that warning was already checked.
In your case it warns BaseRequest<T> is not equivalent to BaseRequest<BaseObj>.
Example:
public class NumberWrapper<N extends Number> {
private N value;
public void setValue(N value) {
this.value = value;
}
}
public class MainClazz {
private NumberWrapper<Integer> wrappedNumber = new NumberWrapper<Integer>();
public void run() {
Number n = externalSource.getNumber();
wrappedNumber.setValue(n); // <-- Error. What if getNumber returns a double?
}
}
You can have this error ir not depending on how you complete/integrate the code you are showing.

Categories

Resources