0

There are things that have weight, so here's their interface, for example:

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

now I want to create things that have weight, so:

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

now, when I want to pick a video by weight, for example:

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

I get a compile error. so what is the correct way to do it?


Hey guys, it's not that easy.

I tried

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

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

And it's still not working, I cannot call the function without cast the class type.

* can someone tell my why Java choose not to let it just work ???*


After reading some basic knowledge to Java Generics, here comes a solution:

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

But declear the function like this, we can simply call:

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

no more cast outside, however, still a cast inside the function. Any better idea?

1
  • Hey guys, I know this sounds easy, but would you please compile the code in IDE first. Commented Aug 30, 2019 at 4:28

4 Answers 4

1

Because List<Banana> is not a List<Video>. Change method signature to:

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

Or better - using common superclass:

@Nullable
public static Banana pickItemByTheirWeight(List<? extends Banana> originalVideos) {
    // ...
    return randomOneByWeight;
}
Sign up to request clarification or add additional context in comments.

13 Comments

what I want to do is to create a function that can handle all kinds of class that implements Banana.
Check out my edit - it works if Video is a subclass of Banana
it's not working, still bad grammar, cannot compile
Can you be mo precise? What's not compiling?
you help me find the "Using Wildcards with Generics" concept, you got it :-)
|
1

You get the error because List<Video> is not List<Banana>. Have a look at this SO question.You could make use of generics and change your method to :

@Nullable
public static <T extends Banana> T pickItemByTheirWeight(List<T> bananas) {
    // ... pick something that implements banana from the list and return it
    return randomOneByWeight;
}

This is when you want to return subtype of Banana from your method. If you do not want to do it, you can get rid of this error by changing your method to :

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

and since it always returns an Banana use it like :

Banana randomOne = pickItemByTheirWeight(videos);

2 Comments

it will use cast. is there a way to avoid cast? can you find a perfect way to do this?
thank you bro, combine your first and your second will produce my final good answer.
0

The pickItemByTheirWeight method expects a list of Banana as a parameter and you give it a list of Video. As a Video is a Banana, you must declare a list of Banana to store your video into.

List<Banana> videos = new ArrayList<>();
// ...
// The method returns a Banana, not a Video
Banana randomOne = pickItemByTheirWeight(videos);
// But you may explicitly cast the Banana as a Video (even if you are not sure the Banana you got from this call is really a Video
Video randomTwo = (Video) pickItemByTheirWeight(videos);

1 Comment

what I want to do is to create a function that can handle all kinds of class that have weight, so the function can easily use the same logic to pick item from list.
0

there are 2 concept of Generics here:

  1. Using Wildcards with Generics for List parameters: since it's already using generics, and you have to use wildcard with generics.

  2. generics function template, it's simple basic.

combining 1 + 2, we get the answer:

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

see my original post edits for more information.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.