sadelbrid sadelbrid - 6 months ago 17
Java Question

How to pass ArrayList<SubClass> as ArrayList<BaseClass>

NOTE: I really have no idea how to ask my question so my title probably didn't do any justice.

I'm beginning to write an RSS parser. I want the user to be able to specify the tag they want information on and my parseInnerXmlByTag method will return an ArrayList of that type. Problem is that different tags have different properties, which means I will have to create classes for each type of tag ("item", "channel", "entry"...). So I decided to create an empty RSSElement class. Then I created an RSSItem class that extends it. So my parseInnerXmlByTag now returns an ArrayList < RSSElements >. My goal is for the user to only call one method specifying the tag they want info about. Say they specify "item", the method should return ArrayList < RSSItem >.

But a line like this won't work because those two types don't techically have a relationship:

ArrayList<RSSElement> data = (method returning ArrayList<RSSItem>)


I'm having trouble wrapping my head around wildcards and subtypes so I was wondering if someone could check out what I have so far and give me your thoughts. Here's my code

public class RSSParser {
public static ArrayList<RSSElement> parseInnerXmlByTag(String in, String tag)
throws XmlPullParserException, IOException{
ArrayList<RSSElement> data = new ArrayList<>();
StringReader input = new StringReader(in);
try{
XmlPullParser parser = Xml.newPullParser();
parser.setInput(input);
parser.nextTag();
data = readFeed(parser, tag);
}
finally {
input.close();
return data;
}
}

/*
Returns an ArrayList of parsed RSSElements specified by "tag"
*/
private static ArrayList<RSSElement> readFeed(XmlPullParser parser, String tag)
throws XmlPullParserException, IOException{
ArrayList<RSSElement> data = new ArrayList<>();
int event = parser.getEventType();

//Parse until input is consumed
while(event != XmlPullParser.END_DOCUMENT){
switch (event){
case XmlPullParser.START_TAG:
if(parser.getName().equalsIgnoreCase(tag)){
RSSItem item = RSSItem.processItem(parser);
data.add(RSSItem.processItem(parser));
}
break;
}
event = parser.next();
}
return data;
}

public static abstract class RSSElement{
//Private constructor prevents instantiation
private RSSElement(){}
}

public static class RSSItem extends RSSElement{
String title = "";
String description = "";
public RSSItem(String t, String d){
title = t;
description = d;
}

@Override
public String toString() {...}

/*
Parses an RSS item tag into a container RSSItem (ONLY GRABS TITLE RIGHT NOW)
*/
protected static RSSItem processItem(XmlPullParser parser)
throws XmlPullParserException, IOException{
String currentTag = "";
String title = "";
/*
while end tag hasn't been reached yet:
reaching the end tag means parser.getEventType == END_TAG && parser.getName = tag
*/
while(!(parser.next() == XmlPullParser.END_TAG && parser.getName().equalsIgnoreCase("item"))){
Log.i("rssparser", "process entry");
if(parser.getEventType() == XmlPullParser.START_TAG){
currentTag = parser.getName();
}
else if(parser.getEventType() == XmlPullParser.TEXT && currentTag.equalsIgnoreCase("title")){
title = parser.getText();
break;
}
}
return new RSSItem(title, description);
}
}
}


Now this code completes and returns the ArrayList. Also I know it's parsing RSSItems correctly based on my logs. But here is where I use it (aka the reason I'm posting here)

@Override
public void onResponse(String response){
ArrayList<? extends RSSParser.RSSElement> items = null;
try {
items = RSSParser.parseInnerXmlByTag(response, "entry");
}...

if(items != null) {
for(int i = 0; i < items.size(); i++){
//****DOESNT WORK****
redditOutputTextView.append(items.get(i).title + "\n");
}
}
else redditOutputTextView.setText("Items null");
}


That line there obviously can't work because it doesn't know it has an array of RSSItems. So hopefully someone can figure out what I'm trying to do and make some suggestions.

Answer

Could you please replace your following snippet:

//****DOESNT WORK****
redditOutputTextView.append(items.get(i).title + "\n");

by following statements:

//****WORKS****
Object obj=items.get(i);
if(obj instanceof RSSItem){
    RSSItem rssItem = (RSSItem)obj;
    redditOutputTextView.append(rssItem .title + "\n");
}

and see the result?

Comments