首页 > 解决方案 > 如何使用带有 List 参数的泛型函数

问题描述

有些东西是有重量的,所以这里是它们的界面,例如:

public interface Banana {
    public Double getWeight();
}

    // This function is a static function in a public tool class.
    @Nullable
    public static Banana pickItemByTheirWeight(List<Banana> originalVideos) {
        // ...
        return randomOneByWeight;
    }

现在我想创造有重量的东西,所以:

class Video implements Banana {
   @Override
   public Double getWeight() {
       //....
       return weight;
   }
}

现在,当我想按重量挑选视频时,例如:

List<Video> videos = new ArrayList<>();
// ...
Video randomOne = pickItemByTheirWeight(videos);

我得到一个编译错误。那么正确的方法是什么?


嘿伙计们,这并不容易。

我试过了

public static <T extends Banana> T pickOneByTheirWeight(List<T> domains) {
    // ...
}

public abstract class Banana {
   public abstract Double getWeight();
}

而且它仍然无法正常工作,我无法在不强制转换类类型的情况下调用该函数。

* 有人能告诉我为什么 Java 选择不让它正常工作吗???*


在阅读了Java泛型的一些基础知识之后,这里有一个解决方案:

public static <T extends Banana> T pickOneByTheirWeight(List<? extends Banana> domains) {
        if (domains.isEmpty()) {
            return null;
        }       
        // calculate total weight
        Double totalWeight = domains.stream().map(x -> {
                Double weight = x.getWeight();
                return weight == null ? 0.0 : weight;
            }).reduce((first, second) -> {
            Double firstWeight = first != null ? first : 0.0;
            Double secondWeight = second != null ? second : 0.0;
            return firstWeight + secondWeight;
        }).get();
        // random process
        final Double randomSeed = Math.random() * totalWeight;
        double currentWeight = 0.0;
        for (Banana v: domains) {
            Double weight = v.getWeight();
            weight = weight == null ? 0.0 : weight;
            currentWeight += weight;
            if (currentWeight >= randomSeed) {
                return (T) v;
            }
        }
        // it'll not reach here indeed.
        logger.warn("pickDomainByTheirWeight cannot pick an element from list by their weights.");
        return null;
    }

但是像这样清除函数,我们可以简单地调用:

List<Video> videos = new ArrayList<>();
// ...
Video p = Utility.pickOneByTheirWeight(videos);

然而,没有更多的外部转换,仍然是函数内部的转换。有更好的主意吗?

标签: javaoopgenericsarraylistjava-8

解决方案


因为List<Banana>不是一个List<Video>。将方法签名更改为:

@Nullable
public static Banana pickItemByTheirWeight(List<Video> originalVideos) {
    // ...
    return randomOneByWeight;
}

或者更好 - 使用通用超类:

@Nullable
public static Banana pickItemByTheirWeight(List<? extends Banana> originalVideos) {
    // ...
    return randomOneByWeight;
}

推荐阅读