将三个相似的方法使用泛型创建一个整洁的方法。

huangapple go评论41阅读模式
英文:

Create a neat method out of three similar ones using generics

问题

我已经尝试过使用泛型做一些事情,但似乎我个人找不到任何简单的解决方案。尽管如此,我认为将这三个类似的方法保持不变是不合适的。

public List<PassengerPlane> getPassengerPlanes() {
    List<PassengerPlane> passengerPlanes = new ArrayList<>();
    for (Plane plane : planes) {
        if (plane instanceof PassengerPlane) {
            passengerPlanes.add((PassengerPlane) plane);
        }
    }
    return passengerPlanes;
}

public List<MilitaryPlane> getMilitaryPlanes() {
    List<MilitaryPlane> militaryPlanes = new ArrayList<>();
    for (Plane plane : planes) {
        if (plane instanceof MilitaryPlane) {
            militaryPlanes.add((MilitaryPlane) plane);
        }
    }
    return militaryPlanes;
}

public List<ExperimentalPlane> getExperimentalPlanes() {
    List<ExperimentalPlane> experimentalPlanes = new ArrayList<>();
    for (Plane plane : planes) {
        if (plane instanceof ExperimentalPlane) {
            experimentalPlanes.add((ExperimentalPlane) plane);
        }
    }
    return experimentalPlanes;
}
英文:

I've tried to do some stuff with generics already but it seems I cannot personally find any simple solution. Still I think it'd be a sin to leave these 3 similar methods alone as they are.

    public List&lt;PassengerPlane&gt; getPassengerPlanes() {
        List&lt;PassengerPlane&gt; passengerPlanes = new ArrayList&lt;&gt;();
        for (Plane plane : planes) {
            if (plane instanceof PassengerPlane) {
                passengerPlanes.add((PassengerPlane) plane);
            }
        }
        return passengerPlanes;
    }

    public List&lt;MilitaryPlane&gt; getMilitaryPlanes() {
        List&lt;MilitaryPlane&gt; militaryPlanes = new ArrayList&lt;&gt;();
        for (Plane plane : planes) {
            if (plane instanceof MilitaryPlane) {
                militaryPlanes.add((MilitaryPlane) plane);
            }
        }
        return militaryPlanes;
    }

    public List&lt;ExperimentalPlane&gt; getExperimentalPlanes() {
        List&lt;ExperimentalPlane&gt; experimentalPlanes = new ArrayList&lt;&gt;();
        for (Plane plane : planes) {
            if (plane instanceof ExperimentalPlane) {
                experimentalPlanes.add((ExperimentalPlane) plane);
            }
        }
        return experimentalPlanes;
    }

答案1

得分: 8

以下是翻译好的部分:

你需要的是通用方法,但问题是 instanceof 无法检查类型参数(在编译期实际上被擦除了),它需要实际的类引用。因此,你可以将它显式提供给方法:

public <T extends Plane> List<T> getPlanes(Class<T> claz) {
  List<T> result = new ArrayList<>();
  for (Plane plane : planes) {
    if (claz.isInstance(plane)) {
      result.add(claz.cast(plane));
    }
  }
  return result;
}

注意如何将 instanceof 和显式转换更改为调用 .isInstance().cast()

使用方法如下:

getPlanes(PassengerPlane.class)
英文:

What do you need is generic method, but the problem is that instanceof cannot check against type parameter (it is in fact erased during compilation), it requires actual class reference. So, you may provide this to the method explicitly:

public &lt;T extends Plane&gt; List&lt;T&gt; getPlanes(Class&lt;T&gt; claz) {
  List&lt;T&gt; result = new ArrayList&lt;&gt;();
  for (Plane plane : planes) {
    if (claz.isInstance(plane)) {
      result.add(claz.cast(plane));
    }
  }
  return result;
}

Note how instanceof and explicit cast changed to calls to .isInstance() and .cast()

Use it like

getPlanes(PassengerPlane.class)

答案2

得分: 2

你可以使用Streams来简化一些操作,但我不确定是否有方法可以避免在这里使用instanceof

public List<PassengerPlane> getPassengerPlanes() {
    return planes.stream().filter(t -> t instanceof PassengerPlane)
                 .map(t -> (PassengerPlane) t).collect(Collectors.toList());
}
public List<MilitaryPlane> getMilitaryPlanes() {
    return planes.stream().filter(t -> t instanceof MilitaryPlane)
                 .map(t -> (MilitaryPlane) t).collect(Collectors.toList());
}
public List<ExperimentalPlane> getExperimentalPlanes() {
    return planes.stream().filter(t -> t instanceof ExperimentalPlane)
                 .map(t -> (ExperimentalPlane) t).collect(Collectors.toList());
}
英文:

You can make things a bit shorter with Streams, but I'm not sure there's a way to get around using instanceof here:

public List&lt;PassengerPlane&gt; getPassengerPlanes() {
    return planes.stream().filter(t -&gt; t instanceof PassengerPlane)
                 .map(t -&gt; (PassengerPlane) t).collect(Collectors.toList());
}
public List&lt;MilitaryPlane&gt; getMilitaryPlanes() {
    return planes.stream().filter(t -&gt; t instanceof MilitaryPlane)
                 .map(t -&gt; (MilitaryPlane) t).collect(Collectors.toList());
}
public List&lt;ExperimentalPlane&gt; getExperimentalPlanes() {
    return planes.stream().filter(t -&gt; t instanceof ExperimentalPlane)
                 .map(t -&gt; (ExperimentalPlane) t).collect(Collectors.toList());
}

答案3

得分: 2

以下是我将如何使用泛型来解决这个问题的方法:

public <T> List<T> getTPlanes(Class<T> clazz) { //声明一个接受泛型类型的方法
    List<T> tPlanes = new ArrayList<>(); //初始化一个相应类型的ArrayList

    planes.stream() //流处理飞机列表
            .filter(clazz::isInstance) //将其筛选为我们想要的类型的飞机
            .forEach((p) -> tPlanes.add((T) p)); //将流中的每个飞机添加到我们新创建并填充的ArrayList中,并进行泛型转换

    return tPlanes; //返回我们刚刚创建和填充的ArrayList
}

希望对你有所帮助。

英文:

Here's how I would approach the problem using generics:

public &lt;T&gt; List&lt;T&gt; getTPlanes(Class&lt;T&gt; clazz) { //declare the method to take a type generic
    List&lt;T&gt; tPlanes = new ArrayList&lt;&gt;(); //initialize an ArrayList of that type

    planes.stream() //stream the planes list
            .filter(clazz::isInstance) //filter it down to only planes of the type that we want
            .forEach((p) -&gt; tPlanes.add((T) p)); //add each plane left in the stream to our new ArrayList, and cast it to the type generic

    return tPlanes; //return the ArrayList we just created and populated
}

答案4

得分: 1

以下是您要翻译的部分:

import java.util.*;
import java.util.stream.*;

public class Example {
    public static class Plane { }
    public static class PassengerPlane extends Plane { }
    public static class MilitaryPlane extends Plane { }
    public static class ExperimentalPlane extends Plane { }

    private static List<Plane> planes =
        List.of(new PassengerPlane(),
                new MilitaryPlane(),
                new ExperimentalPlane());

    public static <T extends Plane> List<T> getPlanesOfType(Class<T> type, List<Plane> planes) {
        List<T> list =
            planes.stream()
            .filter(t -> type.isAssignableFrom(t.getClass()))
            .map(t -> type.cast(t))
            .collect(Collectors.toList());

        return list;
    }

    public static void main(String[] args) throws Exception {
        System.out.println(getPlanesOfType(PassengerPlane.class, planes));
        System.out.println(getPlanesOfType(MilitaryPlane.class, planes));
        System.out.println(getPlanesOfType(ExperimentalPlane.class, planes));
        System.out.println(getPlanesOfType(Plane.class, planes));
    }
}
[Example$PassengerPlane@7b227d8d]
[Example$MilitaryPlane@7219ec67]
[Example$ExperimentalPlane@45018215]
[Example$PassengerPlane@7b227d8d, Example$MilitaryPlane@7219ec67, Example$ExperimentalPlane@45018215]

您可以使用单一方法替代所有三个或使用它来实现。

英文:

You do need to do a cast somewhere: Here is a solution with a single method that takes the subtype.

import java.util.*;
import java.util.stream.*;

public class Example {
    public static class Plane { }
    public static class PassengerPlane extends Plane { }
    public static class MilitaryPlane extends Plane { }
    public static class ExperimentalPlane extends Plane { }

    private static List&lt;Plane&gt; planes =
        List.of(new PassengerPlane(),
                new MilitaryPlane(),
                new ExperimentalPlane());

    public static &lt;T extends Plane&gt; List&lt;T&gt; getPlanesOfType(Class&lt;T&gt; type, List&lt;Plane&gt; planes) {
        List&lt;T&gt; list =
            planes.stream()
            .filter(t -&gt; type.isAssignableFrom(t.getClass()))
            .map(t -&gt; type.cast(t))
            .collect(Collectors.toList());

        return list;
    }

    public static void main(String[] args) throws Exception {
        System.out.println(getPlanesOfType(PassengerPlane.class, planes));
        System.out.println(getPlanesOfType(MilitaryPlane.class, planes));
        System.out.println(getPlanesOfType(ExperimentalPlane.class, planes));
        System.out.println(getPlanesOfType(Plane.class, planes));
    }
}
[Example$PassengerPlane@7b227d8d]                                                                                                                                                                                                      
[Example$MilitaryPlane@7219ec67]                                                                                                                                                                                                       
[Example$ExperimentalPlane@45018215]                                                                                                                                                                                                   
[Example$PassengerPlane@7b227d8d, Example$MilitaryPlane@7219ec67, Example$ExperimentalPlane@45018215]

You could either use the single method to replace all three or use it to implement.

答案5

得分: 1

以下是您要翻译的代码部分:

这可以通过引入一个执行通用部分的方法来完成
```java
private static &lt;T&gt; List&lt;T&gt; createFilteredList(List&lt;Plane&gt; inList, Class&lt;T&gt; clazz) {
    List&lt;T&gt; outList = new ArrayList&lt;&gt;();
    for (Plane value : inList) {
        if (clazz.isInstance(value)) {
            outList.add(clazz.cast(value));
        }
    }
    return outList;
}

然后可以像这样使用它:

public List&lt;PassengerPlane&gt; getPassengerPlanes() {
    return createFilteredList(planes, PassengerPlane.class);
}
public List&lt;MilitaryPlane&gt; getPassengerPlanes() {
    return createFilteredList(planes, MilitaryPlane.class);
}
public List&lt;ExperimentalPlane&gt; getPassengerPlanes() {
    return createFilteredList(planes, ExperimentalPlane.class);
}

请注意,我已经将HTML编码的字符(如`&lt;`和`&gt;`)保留在原文中,以保持代码的完整性。
<details>
<summary>英文:</summary>
It can be done in this way by introduced a method that do common part:

private static <T> List<T> createFilteredList(List<Plane> inList, Class<T> clazz) {
List<T> outList = new ArrayList<>();
for (Plane value : inList) {
if (clazz.isInstance(value)) {
outList.add(clazz.cast(value));
}
}
return outList;
}

Then it can be used like this:

public List<PassengerPlane> getPassengerPlanes() {
return createFilteredList(planes, PassengerPlane.class);
}
public List<MilitaryPlane> getPassengerPlanes() {
return createFilteredList(planes, MilitaryPlane.class);
}
public List<ExperimentalPlane> getPassengerPlanes() {
return createFilteredList(planes, ExperimentalPlane.class);
}


</details>
# 答案6
**得分**: 1
如果你的问题确实很简单,可能不值得付出努力。然而,这是Visitor模式的一个典型问题(特别是如果你的重复代码较多)。
## 步骤1 ##
创建一个`Visitor`接口来访问每种类型的`Plane`:
```java
interface Visitor {
void visit(MilitaryPlane militaryPlane);
void visit(ExperimentalPlane experimentalPlane);
void visit(PassengerPlane passengerPlane);
}

... 并以一种方式实现它,从一个可以通过每个.visit()进行丰富的List<Plane> 开始:

class PlaneVisitor implements Visitor {

    private final List<Plane> planes;

    PlaneVisitor(List<Plane> planes) {
        this.planes = requireNonNull(planes);
    }

    @Override
    public void visit(MilitaryPlane militaryPlane) {
        planes.add(militaryPlane);
    }

    @Override
    public void visit(ExperimentalPlane experimentalPlane) {
        planes.add(experimentalPlane);
    }

    @Override
    public void visit(PassengerPlane passengerPlane) {
        planes.add(passengerPlane);
    }

    public List<Plane> getPlanes() {
        return planes;
    }
}

步骤2 - 在你的类中启用访问者

在你的基类Plane中添加一个abstract方法来接受访问者:

public abstract class Plane {
    //...
    abstract void accept(Visitor visitor);
    //...
}

然后在每个子类中实现这个方法,让Visitor实例访问自身(this)。以PassengerPlane为例(所有其他类的逻辑相同):

public class PassengerPlane extends Plane {
    //...
    @Override
    void accept(Visitor visitor) {
        visitor.visit(this);
    }
    //...
}

步骤3 - 调整你的函数

现在,你的函数可以循环遍历planes列表,不需要关心类型。它将由visitor来解析:

public List<Plane> getPlanes() {
    PlaneVisitor planeVisitor = new PlaneVisitor(new ArrayList<>());
    for (Plane plane : planes) {
        plane.accept(planeVisitor);
    }
    return planeVisitor.getPlanes();
}

请注意,你需要添加一些方法/接口来实现这一点。由于你的代码非常简单,即使不太"优雅",也可以考虑保持现状。然而,上面的示例可以在你的代码实际上需要比你展示给我们的更多功能时提供一些灵感。

英文:

If your problem is really so short, probably it won't be worthy the effort. However, this is a typical problem for Visitor Pattern (especially if your duplicate code is larger).

Step 1

Create a Visitor interface to visit each type of Plane:

interface Visitor {
void visit(MilitaryPlane militaryPlane);
void visit(ExperimentalPlane experimentalPlane);
void visit(PassengerPlane passengerPlane);
}

... and implement it in a way that starts with a List&lt;Plane&gt; that can be enriched by each of the .visit():

class PlaneVisitor implements Visitor {
private final List&lt;Plane&gt; planes;
PlaneVisitor(List&lt;Plane&gt; planes) {
this.planes = requireNonNull(planes);
}
@Override
public void visit(MilitaryPlane militaryPlane) {
planes.add(militaryPlane);
}
@Override
public void visit(ExperimentalPlane experimentalPlane) {
planes.add(experimentalPlane);
}
@Override
public void visit(PassengerPlane passengerPlane) {
planes.add(passengerPlane);
}
public List&lt;Plane&gt; getPlanes() {
return planes;
}
}

Step 2 - Enable visitors in your classes

Add an abstract method in your base class Plane to accept the visitor:

public abstract class Plane {
//...
abstract void accept(Visitor visitor);
//...
}

Then implement this method in each sub-class to let the Visitor instance visit itself (this). Example for PassengerPlane (same logic for all the other classes):

public class PassengerPlane extends Plane {
//...
@Override
void accept(Visitor visitor) {
visitor.visit(this);
}
//...
}

Step 3 - Adapt your function

Your function can now loop through the list of planes not caring about the type. It will be resolved by the visitor:

public List&lt;Plane&gt; getPlanes() {
PlaneVisitor planeVisitor = new PlaneVisitor(new ArrayList&lt;&gt;());
for (Plane plane : planes) {
plane.accept(planeVisitor);
}
return planeVisitor.getPlanes();
}

> Note that you need to add a bit of methods / interfaces to do this. Since your code is very small, you can imagine to leave it like it is even if it's not very "elegant". However, the above example can be of inspiration if your code is actually supposed to do more than what you're showing us.

答案7

得分: 1

所以,您有一个以Plane为输入的可迭代对象。
Plane可以是PassengerPlane、MilitaryPlane或ExperimentalPlane。

您正在尝试通过谓词对飞机集合进行过滤。谓词是一个接受Plane并回答true或false的函数。过滤器使用谓词来确定是否将给定的项目包括在结果中或跳过。

如果您使用Java 8或更高版本,可以使用Streams API。
https://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html

  1. 从飞机可迭代对象产生一个流。
  2. 对其应用过滤器(中间操作)。
  3. 将结果收集到列表中。

使用Stream API,您可以将上述方法转换为一行代码。像这样:

planes.stream().filter(plane -&gt; plane instanceof MilitaryPlane).collect(toList());

然后,可能就不需要单独的整洁方法了。
但是,如果您想要一些可重用的代码片段,那么您必须弄清楚这里的参数是什么。在上面的代码中,它是一个特定的飞机实现:

public List&lt;Plane&gt; filterPlanes(Iterable&lt;Plane&gt; planes, Class&lt;? extends Plane&gt; planeImplementation)

因此,您可以使用这个参数构建一个谓词:

plane -&gt; planeImplementation.isInstance(plane)

英文:

So, you have an iterable of Plane as an input.
A Plane can be PassengerPlane, MilitaryPlane or ExperimentalPlane.

What you are trying to do is to filter a collection of planes by a predicate. A predicate is a function that takes a Plane and answers true or false. A filter uses a predicate to figure out whether to include a given item into the result or to skip.

If you are using Java 8 or later version, you can use the Streams API.
https://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html

  1. Produce a stream from the planes iterable.
  2. Apply filter to it (intermediate operation).
  3. Collect the results to list.

Using Stream API you can convert the methods above into one-liners. Like this:

planes.stream().filter(plane -&gt; plane instanceof MilitaryPlane).collect(toList());

Then, probably, you won't need a separate neat method for it.
But if you want some reusable piece of code, then you have to figure out what is the parameter here. In the code above it is a specific plane implementation:

public List&lt;Plane&gt; filterPlanes(Iterable&lt;Plane&gt; planes, Class&lt;? extends Plane&gt; planeImplementation)

So, you can build a predicate with this parameter:

plane -&gt; planeImplementation.isInstance(plane)

答案8

得分: 0

如果你有一个Plane的超类,你可以让子类继承getPlanes()方法。this.getClass将从列表中提取调用该方法的子类的飞机。这样,你不需要将一个类传递给该方法,因为它可以从调用它的子类派生出来。

public abstract class Plane {

    public Plane(){}

    public List<Plane> getPlanes() {
        List<Plane> result = new ArrayList<>();
        for (Plane plane : planes) {
            if (this.getClass().isInstance(plane)) {
                result.add(this.getClass().cast(plane));
            }
        }
        return result;
    }
}

class PassengerPlane extends Plane {
}
class MilitaryPlane extends Plane {
}
class ExperimentalPlane extends Plane {
}
public class PlaneList {
    public String name;
    public static ArrayList<Plane> planes = new ArrayList<>();

    public PlaneList(){
        planes.add(new MilitaryPlane());
        planes add(new MilitaryPlane());
        planes.add(new MilitaryPlane());
        planes.add(new PassengerPlane());
        planes.add(new PassengerPlane());
        planes.add(new ExperimentalPlane());
    }
}

我像这样测试它:

public class Main {

    public static void main(String[] args) {
	    PlaneList list = new PlaneList();
	    Plane plane = new PassengerPlane();

	    for(Plane p : plane.getPlanes()){
            System.out.println(p.toString());
        }
    }
}

输出:

com.company.PassengerPlane@7dc36524
com.company.PassengerPlane@35bbe5e8
英文:

If you have a Plane supertype, you can make the subclasses inherit the getPlanes() method. this.getClass will extract only the planes of the subclass calling the method from the list. This way, you don't have to pass a class to the method, as it can be derived from the subclass that is calling it.

public abstract class Plane {

    public Plane(){}

    public List&lt;Plane&gt; getPlanes() {
        List&lt;Plane&gt; result = new ArrayList&lt;&gt;();
        for (Plane plane : planes) {
            if (this.getClass().isInstance(plane)) {
                result.add(this.getClass().cast(plane));
            }
        }
        return result;
    }
}

class PassengerPlane extends Plane {
}
class MilitaryPlane extends Plane {
}
class ExperimentalPlane extends Plane {
}
public class PlaneList {
    public String name;
    public static ArrayList&lt;Plane&gt; planes = new ArrayList&lt;&gt;();

    public PlaneList(){
        planes.add(new MilitaryPlane());
        planes.add(new MilitaryPlane());
        planes.add(new MilitaryPlane());
        planes.add(new PassengerPlane());
        planes.add(new PassengerPlane());
        planes.add(new ExperimentalPlane());
    }
}

I tested it like so:

public class Main {

    public static void main(String[] args) {
	    PlaneList list = new PlaneList();
	    Plane plane = new PassengerPlane();

	    for(Plane p : plane.getPlanes()){
            System.out.println(p.toString());
        }
    }
}

output:

com.company.PassengerPlane@7dc36524
com.company.PassengerPlane@35bbe5e8

huangapple
  • 本文由 发表于 2020年7月29日 03:00:45
  • 转载请务必保留本文链接:https://go.coder-hub.com/63141071.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定