简体   繁体   中英

How to detect class difference at compile time using generics (java)

I am using java 8 and I would like to detect subtle class differences at compile time modifying withProperty() header. This code is working but I would like to force a compilation error in main() function because this::getInteger returns an Integer and the second argument is a String.

import java.util.function.Function;

public class MatcherProperty<T, K> {
    public static <T, K> MatcherProperty<T, K> withProperty(
            Function<T, K> mapper,
            K expected
    ){
        return new MatcherProperty<>();
    }

    private Integer getInteger(Object object) {
        return 1;
    }

    public void main() {
        withProperty(this::getInteger, "Random string");
    }
}

I would like to avoid (if possible) a third argument in withProperty() function specifying the class type or something like this. Maybe K is translated to Object, the superclass of Integer an String. What is actually happening under the hoods? Is it possible to force a compilation error in this case?

Thanks in advance.

There is no compile error in your current code because the result of the call to withProperty is ignored.

If you would try to assign the result like this:

MatcherProperty<Object, Integer> mp = withProperty(this::getInteger, "Random string");

then you'd get a compilation error because the String argument doesn't match type K which is Integer in the result.

If you would try to assign the result like this:

MatcherProperty<Object, String> mp = withProperty(this::getInteger, "Random string");

then you'd get a compilation error because the Integer result of the function given as first argument doesn't match type K which is String in the result.

You can only make the assignment compile by using a common super type such as Object or Serializable :

MatcherProperty<Object, Serializable> mp = withProperty(this::getInteger, "Random string");

You can't force people to assign the result of course. You can add a Class<K> parameter to make them choose a class (such as Integer.class or String.class ) but even then, they can just pass Serializable.class or Object.class instead:

public static <T, K> MatcherProperty<T, K> withProperty(
                Class<K> clazz,
                Function<T, K> mapper,
                K expected
        )


withProperty(String.class, this::getInteger, "Random string"); // doesn't compile

withProperty(Integer.class, this::getInteger, "Random string"); // doesn't compile

withProperty(Serializable.class, this::getInteger, "Random string"); // compiles

If you don't tell the compiler somehow what type K is (using class argument or assignment of the return value which is of type K ) then it will infer the common type, Serializable in this case.

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