Need help in understanding java generics concept - java

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.

Related

Java generic List with genric items as parameter

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.

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

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.

Unable to call static method of class received as an upper-bounded ("extends") generic in Java 6?

I have problem with templates and static methods a in Java (version 6), which may be divided into two sub-problems:
First, I need to find a way how I can return a templated Iterable (out of static context) that creates new instances of some sub class (e.g., B) of an abstract class (e.g., A) in every iteration. (Background: I need to translate one object (e.g., a String) from an iterable into another object). I found a way with templates/generics:
import java.util.HashSet;
import java.util.Iterator;
public class Test {
public static <T extends A> Iterable<T> getIterable(final Iterator<String> i) {
return new Iterable<T>() {
public Iterator<T> iterator() {
return new Iterator<T>() {
public boolean hasNext() {
return i.hasNext();
}
public T next() {
try {
/* this is where things go wrong: *
* T is considered as class A and not B */
return T.factory(i.next());
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public void remove() {
i.remove();
}
};
}
};
}
public static void main(String[] args) {
HashSet<String> st = new HashSet<String>();
st.add("x1");
Iterable<B> bi = getIterable(st.iterator());
for (B b : bi)
{
System.out.println(b);
}
}
}
Moreover, I have defined the following class hierarchy:
/* super class */
abstract class A
{
public static <T extends A> T factory(String c)
{
/* returns nothing, should never be called */
return null;
}
}
/* child class */
class B extends A
{
String s;
public B (String s) { this.s = s; }
public static B factory(String s)
{
return new B(s);
}
}
The overall way seems to work (in principle). However, the static method factory called will be always the one in the super class A, even though the template T is of type B.
I am looking for any ideas/suggestions of how to call the factory of the sub class, i.e., of the class that comes with the template <T> (e.g., B). Any help is highly appreciated!
(Note: I found that in Java 7, one could use interfaces and inherit/override static methods, but I am bound to Java 6 ...)
The Java keyword static has a meaning to the compiler: It allows the compiler to statically bind a method call to a method at compile time. Therefore the compiler writes a call to the method A.factory(String) into the produced byte code when compiling the line return T.factory(i.next()).
You obviously wanted to override the factory method in B to have a dynamic behavior when running the program. For that behavior, you must remove the static keyword.

Generic class implementing Iterable

I want to have a generic class that implements Iterable (let's call it ImplIterable) of type T that implements an Iterable interface over some class (that isn't of the generic class type); for example:
public class ImplIterable <T> implements Iterable<A> {
private A[] tab;
public Iterator<A> iterator() {
return new ImplIterator();
}
// doesn't work - but compiles correctly.
private class ImplIterator implements Iterator<A> {
public boolean hasNext() { return true; }
public A next() { return null; }
public void remove() {}
}
}
Where A is some class. Now, this code won't compile:
ImplIterable iable = new ImplIterable();
for (A a : iable) {
a.aStuff();
}
But this will:
Iterable<A> = new ImplIterable();
for (A a : iable) {
a.aStuff();
}
I don't understand why the latter doesn't compile and why can't I iterate over ImplIterable if it properly implements iterable. Am I doing something wrong/is there some workaround for this type of problems?
When you use a generic class without a generic parameter, all generics in that class are disabled.
Since ImplIterable is generic, and you're using it as a non-generic class, the generic parameters inside of it vanish, and it becomes an Iterable (non-generic) of Objects.

Categories

Resources