简体   繁体   中英

How to cast Generic parameters from another generic class argument?

I would like to cast Object T to T2 when a specific type of T2 is passed. T2 is a class from interface T, so there can be multiple implementations of T2. As far as casting, it's happening properly. But I am unable to access the functions of the typecasted function. Am I doing anything wrong?

protected <T, T2> SortedMap<Integer, T2> noNameFunction(List<T> things, T2 ts) {
    SortedMap<Integer, Object> sortedMap = new TreeMap <> (  );
    for (T t: things) {
        T2 as = ( (T2) ts );
        sortedMap.put ( as.getSequence(), as.getFunctionsFromCastedIbjectA() );
    }
    return sortedMap;
}

Am I wrong conceptually somewhere? How do I solve this problem?

Effectively, you cannot invoke any method from as that the compiler is not able to determine at compilation time based on the constraints you impose on T2 .

In your question T2 could be anything extending Object so you can only call methods declared therein like toString , hashCode etc... pretty useless.

If for whatever reason you can narrow down the T2 type (thus T ) to extend a more concrete class or interface then you would gain access to its member methods, For example imagine that T, thus T2, is guaranteed to be Number so you could call intValue .

That probably would solve you problems with T methods such as getSequence as I presume that in fact T is bounded in your code to be some class that defines the getSequence method. Let's say that it called SequenceContainer .

protected <T extends SequenceContainer, T2 extends T>  SortedMap<Integer, T2> noNameFunction(List<T> things, T2 ts) {
    SortedMap<Integer, Object> sortedMap = new TreeMap <> (  );
    for (T t: things) {
        T2 as = ( (T2) t ); 
        sortedMap.put ( as.getSequence(), as.t2OwnMethod() ); // fail to compile, due to the call to t2OwnMethod.
    }
    return sortedMap;
}

The full solution is to use a lambda that allows the caller to provide the T2 code that needs to be invoked inside the method.

protected <T extends SequenceContainer, T2 extends T, X>  SortedMap<Integer, X> noNameFunction(List<T> things, T2 ts, Function<T2, X> valueFunction) {
    SortedMap<Integer, Object> sortedMap = new TreeMap <> (  );
    for (T t: things) {
        T2 as = ( (T2) t ); 
        sortedMap.put ( as.getSequence(), valueFunction.apply(as)); // fail to compile, due to the call to t2OwnMethod.
    }
    return sortedMap;
}

Notice that I have taken this opportunity to generalize the value of the returned sorted-map to a third type X ... this could be simply T2 and will be solved seamlessly at compilation time depending what lambda you provide. Now the way you would invoke this signature assuming that t2OwnMethod returns ... a String say, is:

List<? extends SequenceContainer> scs = ...;
SpecialSequenceContainer exampleSSC = ...; /// SpecialSequenceContainer extends 
SortedMap<Integer, String> option1 = noNameFunction(scs, exampleSSC, ssc -> ssc.t2OwnMethod());
// or 
SortedMap<Integer, String> option2 = noNameFunction(scs, exampleSSC, 
    SpecialSequenceContainer::t2OwnMethod);

Now, notice that the second parameter (exampleSSC) is quite useless... so you could simply omit it.

We can go even further here and use Java streams to do all the work in the question code:

final SortedMap<Integer, String> result = myScs.stream()
     .map(t -> (SpecialSequenceContainer) t)
     .collect(SequenceContainer::getSequence, 
              SpecialSequenceContainer::t2OwnMethod, 
              (s1, s2) -> s1, 
              TreeMap::new); 

Notice that (s1, s2) -> s1 lambda would only be invoked if there are key collisions, perhaps that would never be the case but you need to provide the value merging function regardless.

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