简体   繁体   中英

Java - working with list of objects and generics

I have a problem with a simple piece of Java code. I cannot determine if it solves the original purpose; the guy that wrote it (yet unreachable) just told me that "an object that implements IA should be a container ( List ) of IB -like objects”. At first sight I have considered it wrong, because of the strong constraint ( T extends IB<T> ) seems illogical, but the IDE compiler does not show any related error/warning.

If such code is meaningful, could someone please provide an example of practical usage of such interfaces.

Thanks in advance.

import java.util.List;

public interface IA<T extends IB<T>> {
    public List<T> getList();
}

public interface IB<T> {
    public T getValue();
}

UPDATE 1 : added test with concrete sample classes

class Bar implements IA<Foo>{

    List<Foo> list;

    @Override
    public List<Foo> getList() {
        return list;
    }

    Bar(List<Foo> foos) {
        this.list = foos;
    }
}

class Foo implements IB<Foo> {

    public Float data;

    @Override
    public Foo getValue() {
        return foo;
    }

    Foo(Float data){
        this.data = data;
    }

    public Float getV() {
        return data;
    }
}

public class DataTest {

    @Test
    public void myTest() {
        Foo f = new Foo(10F);
        List<Foo> fs = new ArrayList<>();
        fs.add(f);
        Bar bar = new Bar(fs);
        List<Foo> foos = bar.getList();
        System.out.println(foos.get(0).getV());
    }

}

Is this the correct way to use IA and IB?

As T is only used in covariant position, it is safe to use as it is, so the comment on IA can be correct. If IA had a method accepting a T (like int compare(T a, T b) ) in one of its parameters, that would cause problems as it were in a contravariant position.

Such a constraint makes sense in certain circumstances. For example, if you want to make an sorted list class, you might do something like

class SortedList<T extends Comparable<? super T>>

where you require that the element type can be compared to itself, which is necessary for you to sort it. (Note that Comparable itself doesn't have a bound on its type parameter, just like here.)

The super in the thing above is because Comparable is a consumer with respect to T , and so per PECS, you should use super wildcards with Comparable . In your case, since IB is a producer with respect to T , you could make it public interface IA<T extends IB<? extends T>> public interface IA<T extends IB<? extends T>> if you want to make it most general.

As to an actual use case that uses this constraint, here's one I came up with that is a class that uses the constraint:

class Bar<T extends IB<T>> implements IA<T> {

    T start;

    @Override
    public List<T> getList() {
        List<T> result = new ArrayList<T>();
        for (T x = start; x; x = x.getValue()) {
            result.add(x);
        }
        return result;
    }

    Bar(T start) {
        this.start = start;
    }
}

Where you have an implementing class that it itself generic (with the same <T extends IB<T>> bound), and it takes one T and generates more T s until it reaches null , and returns a list of these.

Though this still doesn't require that the interface IA have the constraint, so I guess it still doesn't provide an example where the bound on the parameter of IA is necessary.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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