Java中相同逻辑的推广方法

时间:2018-04-06 05:58:18

标签: java generics refactoring

嗨,假设我们有一个包含许多数组列表的类,并包含非常类似的方法,如下所示

public class Foo {
    private ArrayList<Rapper> rapperList = new ArrayList<>();
    private ArrayList<Musician> musicianList = new ArrayList<>();
    private ArrayList<Stadium> stadiumList = new ArrayList<>();

    public List<String> getRapperName() {
        ArrayList<String> rapperNameList = new ArrayList<>();
        for (Rapper r : rapperList) {
            rapperNameList.add(r.getRapperName());
        }
        return rapperNameList;
    } 

    public List<String> getMusicianName() {
        ArrayList<String> musicianNameList = new ArrayList<>();
        for (Musician m : musicianNameList) {
            musicianNameList.add(m.getMusicianName());
        }
        return musicianNameList;
    } //done

    public List<String> getStadiumID() {
        // exactly the same as above (using same logic)
    }

}

我省略了getStadiumID方法代码,因为它与上面的代码非常相似,并且所有方法都遵循相同的逻辑:

  • 列出清单
  • 循环通过相应的数组
  • 添加到列表
  • 返回列表

我觉得有很多重复的代码,因为我基本上遵循相同的逻辑,并想知道是否有一种方法将这些方法简化为一种。我正在研究java中的泛型,但似乎无法从中获得任何东西。

假设我的知识仅限于Java 7(任何高于7的东西我都不知道lambda等等)

4 个答案:

答案 0 :(得分:2)

基本上,你在这里做的是映射列表中的每个元素:

<link href="https://vjs.zencdn.net/6.7/video-js.css" rel="stylesheet"/>
<script src="https://vjs.zencdn.net/6.7/video.js"></script>
<script src="https://cdn.rawgit.com/videojs/videojs-youtube/c4be481f/dist/Youtube.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<video id="my_video" class="video-js vjs-default-skin" controls preload="auto" width="640" height="400">
</video>
<hr>
<button onclick="playVideo(0)">play 0 (mp4)</button>
<button onclick="playVideo(1)">play 1 (mp4)</button>
<button onclick="playVideo(2)">play 2 (youtube)</button>
<hr>
<div id="info"></div>

您可以将此包装在采用return list.stream().map(fn).collect(Collectors.toList()); List<T> list的方法中,其中Function<T, S> fn是列表元素的类型(例如问题代码中的S

String

然后您可以调用:

private static <T, S> List<S> makeList(List<T> list, Function<T, S> fn) {
  return list.stream().map(fn).collect(Collectors.toList());
}

您可以在Java 8之前的代码中实现这一点;但它不会很漂亮。

首先,您需要一个public List<String> getRapperName() { return makeList(rapperList, Rapper::getRapperName); } public List<String> getMusicianName() { return makeList(musicianList, Musician::getMusicianName); } 界面。您可以使用Guava中的一个,其他一些库;或者只是自己定义一个,如果你不想采取依赖:

Function

然后定义interface Function<T, S> { S apply(T input); } 方法:

makeList

然后调用:

private static <T, S> List<S> makeList(List<T> list, Function<T, S> fn) {
    ArrayList<String> result = new ArrayList<>();
    for (T t : list) {
        result.add(fn.apply(t));
    }
    return result;
}

语法多么混乱。

通过将public List<String> getRapperName() { return makeList(rapperList, new Function<Rapper, String>() { @Override public String apply(Rapper r) { return r.getRapperName(); } }); } public List<String> getMusicianName() { return makeList(musicianList, new Function<Musician, String>() { @Override public String apply(Musician m) { return m.getMusicianName(); } }); } 拉出到常量等等,可以使它看起来更清晰;但老实说,你在这里有更多的代码(以及非惯用代码),而不仅仅是复制你当前的代码。

答案 1 :(得分:1)

好的......当然你可以使用泛型,但你可能需要更改一些东西....我将使用Java 7以防万一,但使用Java 8的东西可以变得容易一些。< / p>

首先直接进入要重写的课程:

public class Foo<T extends NamedObject> { 
//We will get to what is NamedObject in a moment
    private ArrayList<T> list = new ArrayList<>();

    public List<String> getNames() {
        ArrayList<String> nameList = new ArrayList<>();
        for (T r : list) {
            nameList.add(r.getName());
        }
        return nameList;
    } 
}

现在你应该能够使用这样的类,有一个小的编译错误(为什么?因为我们还没有了解NamedObject应该是什么):

Foo<Rapper> rapperObj = new Foo<>();
rapperObj.getNames();
Foo<Musician > musicianObj = new Foo<>();
musicianObj.getNames();

我们可以定义NamedObject(随意更改适合您的名称)作为定义一个“getName”方法的接口,并使Rapper,Musician和任何拥有名称的类实现此方法。在Rapper课程中使用“rapperName”,在音乐家课程中使用“musicianName”听起来有点多余,在Rapper中只有“名字”应该足以让每个人都像解释者的名字一样解释它(你不觉得)并且让它更容易也适合我们。

public interface NamedObject {
    String getName();
}

public class Rapper implements NamedObject {
    //Instead of rapperName put only name
    @Override
    public String getName() { ... }
    ... //All the things you have in this class
}

没有时间测试它,但这应该可以解决问题。

注意:实际上可能有另一种方法可以在没有接口的情况下解决它,使用Foo作为抽象类,并为每个对象类型设置一个子类

答案 2 :(得分:0)

在Java 8中,我们将使用lambdas。这可以在Java 7中重新编码,如下所示:

public class Foo {
    private ArrayList<Rapper> rapperList = new ArrayList<>();
    private ArrayList<Musician> musicianList = new ArrayList<>();
    private ArrayList<Stadium> stadiumList = new ArrayList<>();

    public List<String> getRapperName() {
        return asStringList(rapperList, new StringGetter<Rapper>() {
            @Override
            public String get(Rapper it) {
                return it.getRapperName();
            }
        });
    }

    public List<String> getMusicianName() {
        return asStringList(musicianList, new StringGetter<Musician>() {
            @Override
            public String get(Musician it) {
                return it.getMusicianName();
            }
        });
    }

    interface StringGetter <T> {
        String get(T it);
    }

    private <T> List<String> asStringList(List<T> list, StringGetter<T> getter) {
        List<String> stringList = new ArrayList<>();
        for(T t: list) {
            stringList.add(getter.get(t));
        }
        return stringList;   
    }

}

答案 3 :(得分:-1)

您还可以将您的类作为可用于处理任何对象的通用类(说唱歌手,音乐家,体育场......)

public class GenericsDemo<T> {
private ArrayList<T> genericList = new ArrayList<>();

public ArrayList<T> getGenericList() {
    return genericList;
}

public void setGenericList(ArrayList<T> genericList) {
    this.genericList = genericList;
}

public List<String> getGenericName() {
    ArrayList<String> genericNameList = new ArrayList<>();
    for (T obj : genericList) {
        Field field = obj.getClass().getDeclaredField("name");
        Object nameValue = field.get(obj);
        genericNameList.add(nameValue );
    }
    return genericNameList;
}

}

从主类中创建此类的对象并将其用于任何类型。

class GenericsMain {
GenericsDemo<Rapper> rapperObj = new GenericsDemo<>();
//create arraylist of any object
//call the setter method
//call the getname method

}