如何将ArrayList <subclass>作为ArrayList <baseclass>传递

时间:2016-05-28 02:27:31

标签: java generics type-inference

注意:我真的不知道如何提出我的问题所以我的头衔可能没有任何正义。

我开始编写RSS解析器了。我希望用户能够指定他们想要的信息,我的parseInnerXmlByTag方法将返回该类型的ArrayList。问题是不同的标签具有不同的属性,这意味着我必须为每种类型的标签创建类(“item”,“channel”,“entry”......)。所以我决定创建一个空的RSSElement类。然后我创建了一个扩展它的RSSItem类。所以我的parseInnerXmlByTag现在返回一个ArrayList&lt; RSSElements&gt;。 我的目标是让用户只调用一个指定他们想要信息的标签的方法。假设他们指定“item”,该方法应返回ArrayList&lt; RSSItem&gt;。

但是这样的一行不起作用,因为这两种类型在技术上没有关系:

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

我在绕过通配符和子类型时遇到了麻烦,所以我想知道是否有人可以查看我到目前为止的内容并告诉我你的想法。这是我的代码

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

现在这段代码完成并返回ArrayList。另外我知道它正在根据我的日志正确解析RSSItems。 但这里是我使用它的地方(也就是我在这里发布的原因)

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

那条线显然无法工作,因为不知道它有一个RSSItem数组。所以希望有人能弄清楚我正在做什么并提出一些建议。

3 个答案:

答案 0 :(得分:2)

请您替换以下代码段:

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

通过以下陈述:

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

并查看结果?

答案 1 :(得分:0)

更改

ArrayList<? extends RSSParser.RSSElement> items = null;

ArrayList<RSSParser.RSSElement> items = null;

基本上,您现在有一个列表可以包含RSSElement

的任何子类的项目

旧的列表是一个只能从RSSElement派生的某些类的项目的列表,并且因为你使用了通配符,所以该类型可以是任何类型,所以编译器不知道。您可以调用get然后使用它,因为您知道元素的超类:RSSElement

有关详细信息,请参阅this answer

答案 2 :(得分:0)

你可以使用这样的返回有趣代码:

public static List<? extends Parent> test2(){
        List<Parent> parents = new ArrayList<Parent>();
        parents.add(new Son1());
        return parents;
}

并获取如下列表:

List<? extends Parent> parents = test2();

和obv,Clss Son1()扩展Parent。