简体   繁体   English

Java generics 问题 - 需要 T,提供捕获,反之亦然

[英]Java generics problem - required T, provided capture or vice-versa

I have an abstract class:我有一个摘要 class:

public abstract class AbstractPoint<PointType extends AbstractPoint<PointType>> {
    public abstract double getX();
    public abstract double getY();
    public abstract double rho();

    // Find the distance between two points
    public final <T extends AbstractPoint<T>> double distance(T other) {
        return vectorTo(other).rho();
    }

    // Find vector from one point to another.
    public abstract <T extends AbstractPoint<T>> PointType vectorTo(T other);

    // other methods ...
}

Classes OrthogonalPoint and PolarPoint extend the abstract class:OrthogonalPointPolarPoint扩展了抽象 class:

public class OrthogonalPoint extends AbstractPoint<OrthogonalPoint> {
    private double x;
    private double y;

    public OrthogonalPoint(double x, double y) {
        this.x = x;
        this.y = y;
    }

    public double getX() { return this.x; }

    public double getY() { return this.y; }

    public double rho() { return Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)); }

    public double theta() { return Math.atan2(this.y, this.x); }

    public <T extends AbstractPoint<T>> OrthogonalPoint vectorTo(T other) {
        return new OrthogonalPoint(other.getX() - this.x, other.getY() - this.y);
    }
}
public class PolarPoint extends AbstractPoint<PolarPoint> {
    private double rho;
    private double theta;

    public PolarPoint(double x, double y) {
        this.rho = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
        this.theta = Math.atan2(y, x);
    }

    public double getX() { return this.rho() * Math.cos(this.theta()); }

    public double getY() { return this.rho() * Math.sin(this.theta()); }

    public double rho() { return this.rho; }

    public double theta() { return this.theta; }

    public <T extends AbstractPoint<T>> PolarPoint vectorTo(T other) {
        return new PolarPoint(other.getX() - this.getX(), other.getY() - this.getY());
    }
}

With this code, I can correctly calculate the distance between points no matter whether they are of same type (orthogonal or polar).使用此代码,我可以正确计算点之间的距离,无论它们是否属于同一类型(正交或极坐标)。
Now I add another class called Route :现在我添加另一个 class 称为Route

public class Route {

    private final List<? extends AbstractPoint<?>> points = new ArrayList<>();

    // 'point' in 'this.points.add(point)' underlined red
    public <T extends AbstractPoint<T>> void appendPoint(T point) { this.points.add(point); }

    public <T extends AbstractPoint<T>> void removePoint(T point) { this.points.remove(point);  }

    // 'point' in '.distance(point)' underlined red
    public double routeDistance() {
        return this.points.stream().skip(1).mapToDouble(point ->
                this.points.get(this.points.indexOf(point) - 1).distance(point)).sum();
    }
}

A route is an ordered number of points whose length is defined as the sum of the distances of the points in the sequence.路线是有序数量的点,其长度定义为序列中点的距离之和。 So, if a route consists of three points (p1, p2, and p3) the distance of the route is p1.distance(p2) + p2.distance(p3) .因此,如果路线由三个点(p1、p2 和 p3)组成,则路线的距离为p1.distance(p2) + p2.distance(p3)
However, some places (look at the comments in Route ) in my code are underlined red:但是,我的代码中有些地方(看Route中的注释)有红色下划线:

// in appendPoint function
Required type: capture of ? extends AbstractPoint<?>
     Provided: T

// in routeDistance function
Required type: T
     Provided: capture of ? extends AbstractPoint<?>

I want to be able to add any type of point to the points list and the call routeDistance function to calculate the length of the route.我希望能够将任何类型的点添加到点列表并调用routeDistance function 来计算路线的长度。 What is wrong with my solution - how should I change my generics (in any class)?我的解决方案有什么问题 - 我应该如何更改我的 generics(在任何课程中)?

First, note that you are both adding data to the points list, and taking data out of the list, so you should not use extends or super .首先,请注意,您既要向points列表添加数据,又要从列表中取出数据,因此不应使用extendssuper (See also: PECS ) (另见: PECS

private final List<AbstractPoint<?>> points = new ArrayList<>();

Next, you will see that there is an error at the .distance(point) call.接下来,您将看到.distance(point)调用出现错误。 This is because there is no type that can be used as the type parameter T of the distance method, that also satisfies the constraint T extends AbstractPoint<T> .这是因为没有类型可以用作distance方法的类型参数T ,它也满足约束T extends AbstractPoint<T> Note that the type of point is AbstractPoint<?> , and AbstractPoint<?> can't be T , because AbstractPoint<?> does not extend AbstractPoint<AbstractPoint<?>> .请注意, point的类型是AbstractPoint<?> ,而AbstractPoint<?>不能是T ,因为AbstractPoint<?>不会扩展AbstractPoint<AbstractPoint<?>> See the problem?看到问题了吗?

From what I can see, the way you have written distance does not allow you to find the distance between two AbstractPoint s.据我所知,您编写distance的方式不允许您找到两个AbstractPoint之间的距离。 One of the points must be a concrete type.其中一个点必须是具体类型。

In fact, distance and vectorTo doesn't actually need the generic parameter.事实上, distancevectorTo实际上并不需要泛型参数。 It can just take a AbstractPoint<?> :它可以只需要一个AbstractPoint<?>

public final double distance(AbstractPoint<?> other) {
    return vectorTo(other).rho();
}

public abstract PointType vectorTo(AbstractPoint<?> other);

This now allows you to pass in AbstractPoint<?> to distance .这现在允许您将AbstractPoint<?>传递给distance

Side note: your current implementation of routeDistance using indexOf is not very efficient, as it goes over the list once for every point in the list.旁注:您当前使用indexOf实现routeDistance的效率不是很高,因为它会针对列表中的每个点遍历一次列表。 You can do this with a zip operation instead.您可以改用 zip操作来执行此操作。 Using the zip method in the linked answer:在链接的答案中使用zip方法:

return zip(points.stream(), points.stream().skip(1), AbstractPoint::distance)
    .mapToDouble(x -> x).sum();

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM