Home > Software design >  Java Generics Function Return Declaration
Java Generics Function Return Declaration

Time:10-06

I am new to Java generics and have managed to get a getKeyByValue function working with a HashMap<String, String>, but I don't understand how the function declaration can be ambiguous/redundant and still work. For example both of these declaration work but the first does not make a lot of sense to me:

    private <T, E> String getKeyByValue(Map<String, E> map, String value) {
        for (Entry<String, E> entry : map.entrySet()) {
            if (value.equals(entry.getValue())) {
                return entry.getKey();
            }
        }
        return null;
    }

Example with just <E>:

    private <E> String getKeyByValue(Map<String, E> map, String value) {
        for (Entry<String, E> entry : map.entrySet()) {
            if (value.equals(entry.getValue())) {
                return entry.getKey();
            }
        }
        return null;
    }

CodePudding user response:

Your first example is just declaring Type parameters that are never used - you can declare as many as you like!

private <T, E, F, G> String getKeyByValue(Map<String, E> map, String value) {
    for (Entry<String, E> entry : map.entrySet()) {
        if (value.equals(entry.getValue())) {
            return entry.getKey();
        }
    }
    return null;
}

CodePudding user response:

For the first sample, the generic format type T is not used, hence can be removed which brings the implementation to be as you second method implementation.

In the second implementation, the Java Generics use is with no utility as the equals method (you are calling with a typed parameter E) for the java.lang.String class accepts an Object argument, hence any object would fit in there.

private <E> String getKeyByValue(Map<String, E> map, String value) {
    for (Map.Entry<String, E> entry : map.entrySet()) {
        if (value.equals(entry.getValue())) { // there is no type checking needed over the value as it can be any acceptable object
            return entry.getKey();
        }
    }
    return null;
}

The same method could be rewritten using a ? wildcard as the formal type since there is no type-bounded operations that are needed over the Map values:

private String getKeyByValue(Map<String, ?> map, String value) {
    for (Map.Entry<String, ?> entry : map.entrySet()) {
        if (value.equals(entry.getValue())) {
            return entry.getKey();
        }
    }
    return null;
}

CodePudding user response:

For the first case, type parameter T is obviously redundant.

From another point of view, a similar case is declaring method input parameter without using it. You may think the compiler should not allow us to do so. But sometimes such "flexibility" can be useful. For example, we may have parent class having a method with input parameter only used in child class. Although I can't think of a scenario for redundant type parameter to be useful, someone may make use of such language flexibility to solve their problem.

I would say the first case should be considered as code smell, just like empty if statement, or declaring unused variable. IDE and linter are the right tool to tackle them, not the compiler.

  • Related