souldzin souldzin - 4 months ago 7
Java Question

Without specifying a generic type, method does not return a list of elements, but simply a list

I am having a slight inconvenience when working with generics in Java. Please consider the following code:

/**
* MyElement class is simply a wrapper for a generic object.
*/

public static class MyElement<T> {
public final T OBJ;

public MyElement(T obj) {
this.OBJ = obj;
}
}

/**
* MyElementList contains an array list of MyElements of the given type, T.
* This represents a class that uses a list of MyElements of a certain type,
* and this list can be accessed in an unmodifiable format.
*/

public static class MyElementList<T> {

//Properties
private List<MyElement<T>> elementList = new ArrayList();

//CTOR
public MyElementList(List<MyElement<T>> initElements) {
elementList.addAll(initElements);
}

//Getter
public List<MyElement<T>> getElements() {
return Collections.unmodifiableList(elementList);
}

}

public static void main(String[] args) {
//New list of elements
//Notice that I did not explicitly specify the type for 'MyElement'
List<MyElement> theElements = new ArrayList(Arrays.asList(
new MyElement[] {
new MyElement("E 1"),
new MyElement("E 2"),
new MyElement("E 3")
}
));

//Also notice I did not explicitly specify the type for 'MyElementList'
MyElementList theList = new MyElementList(theElements);

//The following does not work.
//It seems to not work because theList.getElements() returns a 'List'
//not necessarily a 'List<MyElement>' which is what I would expect it to
//return...
//Why???

for(MyElement e : theList.getElements()) {
System.out.println(e.OBJ.toString());
}

//Currently my work around is to do the following, but I do not like
//having to introduce another variable, and I would rather just do the
//one above

List<MyElement> listOfElements = theList.getElements();
for(MyElement e : listOfElements) {
System.out.println(e.OBJ.toString());
}

//How come the first 'for-each' loop method does not work?
//Is there anyway I could get it to work?
//THANK YOU!
}


In the main method, if I don't specify the type parameter for 'MyElementList' the 'getElements()' method only returns a 'List', not a 'List<MyElement>'. This is inconvenient because if I want to iterate through each 'MyElement' I need to introduce another variable as a temporary list, shown in the code.


  • Why doesn't the 'getElements()' method return a 'List<MyElement>'?

  • Without making significant changes to 'MyElementList' Is there anything I can do to fix this?

  • Is this a bad design practice?



The IDE I am using is Netbeans 7.2

Thanks in advance!

EDIT

Thank you all for your quick responses. I am very impressed with the community here. I have concluded the following:


  • If a generic hint is not specified, Java ignores ALL other associated generic hints for a class - which is kind of lame, but I can live with it.

  • When using generics, it is a best practice to actually specify the generic type when creating an instance of the class. This seems to be the most object oriented solution.



Thanks again!

Answer

If you change MyElementList to look like

public static class MyElementList<T extends MyElement> {

  //Properties
  private List<T> elementList = new ArrayList<T>();

  //CTOR
  public MyElementList(List<T> initElements) {
      elementList.addAll(initElements);
  }

  //Getter
  public List<T> getElements() {
      return Collections.unmodifiableList(elementList);
  }
}

It should work.

EDIT Generics can be seen as compile time hints in Java, since Java erasure will convert generics to Object. Updating your class as above will tell the compiler only elements which extend MyElement fit the list and for(MyElement e : theList.getElements()) will work.

EDIT 2 As pointed out by others (sorry, I didn't see it at first glance) also change the raw declaration to:

MyElementList<MyElement> theList = new MyElementList<MyElement>(theElements);
Comments