Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Do Collections functional methods call get, put, etc?

I've spent many years working with Java 1.6 (maintaining legacy tools) and am just starting to migrate to 1.8. One big change is the functional methods in the java.util.Collections suite. The biggest concern for me is I have several collection extensions which apply careful checks or algorithms on modification. Do the default methods call the already defined put(..), get(...), remove(..) etc functions or do I have to do a major rework to make this work?

E.g. (ignoring null checks, etc. map that only holds values <= 10)

public class LimitedMap extends HashMap<String, Integer>{
    @Override
    public Integer put(String key, Integer value){
        if(value> 10) throw new IllegalArgumentException();
        return super.put(key, value);
    }

    @Override
    public Integer computeIfAbsent(String key, Function<? super String, ? extends Integer> mappingFunction) {
        return super.computeIfAbsent(key, mappingFunction);
    }
}

With this pair of functions: would I still have to do a detailed override and put new checks into the computeIfAbsent function?

like image 813
K Barad Avatar asked May 16 '26 18:05

K Barad


1 Answers

The only way you could be certain that only the interface methods from pre Java 8 can be used is if you somehow could delegate to the default method implementation in the interface (Map<K, V> in this case).

That is, if you could write something like the following (which you can't).

public class LimitedMap extends HashMap<String, Integer> {

    @Override
    public Integer computeIfAbsent(String key,
            Function<? super String, ? extends Integer> mappingFunction) {

        return Map.super.computeIfAbsent(key, mappingFunction);
    }
}

Unfortunately that is not legal since you only can invoke the method that you've overriden (here the one from HashMap<String, Integer>) but not the one which the inherited method might have overridden (these are the normal rules for super method invocation).

So the only workaround I see for your situation is to create a copy of the interface default method implementation in a helper class like this:

public class Maps {

    public static <K, V> V computeIfAbsent(Map<K, V> map,
            K key, Function<? super K, ? extends V> mappingFunction) {
        Objects.requireNonNull(mappingFunction);
        V v;
        if ((v = map.get(key)) == null) {
            V newValue;
            if ((newValue = mappingFunction.apply(key)) != null) {
                map.put(key, newValue);
                return newValue;
            }
        }

        return v;
    }
}

This is the implementation from java.util.Map as a static method enhanced by an additional parameter map for the instance to operate on.

With such a helper class you could now write

public class LimitedMap extends HashMap<String, Integer> {

    @Override
    public Integer computeIfAbsent(String key,
            Function<? super String, ? extends Integer> mappingFunction) {

        return Maps.computeIfAbsent(this, key, mappingFunction);
    }
}

That's not the prettiest solution but one that should work with a limited amount of effort.

like image 157
Anlon Burke Avatar answered May 19 '26 08:05

Anlon Burke



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!