Declaring shared parameters in a static member

Why it is not allowed to define such a static member:

private static final <T extends Object> Map<Class<T>, BiFunction<T, T, Boolean>> SPECIFIC_HANDLERS = new HashMap<>(); 

Instead, he allowed the use of it unspecified:

 private static final Map<Class<?>, BiFunction<?, ?, Boolean>> SPECIFIC_HANDLERS = new HashMap<>(); 

Is there a workaround so that I can determine that both parameters for BiFunction MUST be of the same type and that the map key must be a class type of these parameters?

Updated for clarification (because @Mena's suggestion is not suitable for me):

I want the map for the array to be equal to the method for the general equals helper. The general assistant receives formally two objects. If they are arrays, I must pass them to one of the overloaded Arrays.equals () methods. I tried to find the correct method (example 1):

 private static final Map<Class<?>, BiFunction<?, ?, Boolean>> ARRAY_EQUALS_HANDLER = new HashMap<>(); static { ARRAY_EQUALS_HANDLER.put( Object[].class, (l, r) -> Arrays.equals( (Object[]) l, (Object[]) r )); ARRAY_EQUALS_HANDLER.put( boolean[].class, (l, r) -> Arrays.equals( (boolean[]) l, (boolean[]) r )); .... } 

and then using it like:

 boolean equal = ARRAY_EQUALS_HANDLER.get( anObj1.getClass()).apply(anObj1, anObj2); 

The design (according to Mena) does not even compile:

 private static <T extends Object> Map<Class<T>, BiFunction<T, T, Boolean>> getSpecificHandlers() { Map<Class<T>, BiFunction<T, T, Boolean>> result = new HashMap<>(); result.put( Object[].class, (l, r) -> Arrays.equals( (Object[]) l, (Object[]) r )); result.put( boolean[].class, (l, r) -> Arrays.equals( (boolean[]) l, (boolean[]) r )); return result; } 

And if I fill in the generated map outside the method:

  @SuppressWarnings( { "unchecked", "rawtypes" }) private static final Map<Class<?>, BiFunction<?, ?, Boolean>> ARRAY_EQUALS_HANDLER = (Map) getSpecificHandlers(); static { ARRAY_EQUALS_HANDLER.put( Object[].class, (l, r) -> Arrays.equals( (Object[]) l, (Object[]) r )); ... } 

then the safety of the whole type disappeared because I have to make the (unchecked) type when assigning it to the last static member.

My example 1 works, but I have to use the resulting lambda when using it:

 private static <T extends Object> boolean equalsArray( T anArray, T anOtherArray) { Object o = ARRAY_EQUALS_HANDLER.get( anArray.getClass()); @SuppressWarnings( "unchecked") BiFunction<T, T, Boolean> func = (BiFunction<T, T, Boolean>) o; Boolean result = func.apply( anArray, anOtherArray); return result; } 
+7
java generics
source share
3 answers

You use the generic idiom method (declaring a type parameter before the return type) to declare constants.

This idiom will not compile.

As mentioned elsewhere, you cannot use a generic class type in a static context.

What you can do as a workaround is to declare a static method instead of a constant - something in the lines:

 private static final <T extends WhateverBound> Map<Class<T>, BiFunction<T, T, Boolean>> getSpecificHandlers(T t) { // return new HashMap<Class<T>, BiFunction<T, T, Boolean>>(); // much easier on the eye - thanks Andy Turner return new HashMap<>(); } 

Assuming:

 static class WhateverBound{} static class Foo extends WhateverBound {} 

Then you can call your method like:

 Map<Class<Foo>, BiFunction<Foo, Foo, Boolean>> map = getSpecificHandlers(new Foo()); 

Note, of course, that the final keyword here has a completely different meaning and may be omitted altogether.

Or...

You can save it as a constant and repeat the substitution / border pattern for all parameterization parameters.

For example:

 private static final Map<Class<? extends WhateverBound>, BiFunction<? extends WhateverBound, ? extends WhateverBound, Boolean>> SPECIFIC_HANDLERS = new HashMap<>(); static { SPECIFIC_HANDLERS.put(Foo.class, (f, ff) -> true); } 
+3
source share

I have a solution, it is a little ugly, but it works on request.

First, note that the Map.get() method accepts the raw Object type as an argument, so the compiler cannot guess the return type of this method based on the type of the argument. Instead, the return type comes from the declaration of the field itself and, as it declared using a parameter of a fixed type (in your case it should be Object ), you get a fixed return type for any call to .get() . In fact, the same applies not only to the get() method, but to all that you expect to act as methods with type parameters.

Secondly, the desired structure should add some restrictions on key pairs and values ​​(you want the type of value to depend on the type of key). This is usually done using type parameters. It does not work to declare a field, but works to declare a type or method.

So, with these two premieres, I finish the decision: enter a type that extends Map with an additional type correlation and with an additional general get() method (you can, of course, add more general methods).

 public static class HandlerMap<T> extends HashMap<Class<? extends T>, BiFunction<T, T, Boolean>> { @SuppressWarnings("unchecked") <U extends T> BiFunction<U, U, Boolean> getStrict(Class<? extends U> key) { return (BiFunction<U, U, Boolean>) get(key); } } private static HandlerMap<Object> ARRAY_EQUALS_HANDLER = new HandlerMap<>(); static { ARRAY_EQUALS_HANDLER.put(Object[].class, (l, r) -> Arrays.equals((Object[]) l, (Object[]) r)); ARRAY_EQUALS_HANDLER.put(boolean[].class, (l, r) -> Arrays.equals((boolean[]) l, (boolean[]) r)); //WARNING - type safety breaks here ARRAY_EQUALS_HANDLER.put(int[].class, (l, r) -> Arrays.equals((boolean[]) l, (boolean[]) r)); } public static void main(String[] args) throws Exception { BiFunction<int[], int[], Boolean> biFunction = ARRAY_EQUALS_HANDLER.getStrict(int[].class); } 

I used HashMap as a superclass for shorter code. Note that the getStrict() method is marked as unsafe (obviously, it is). Also note the lack of coherence of the put() .

Now I think that all this ugliness comes from the fact that you really do not want a Map , but something else. Yes, there is a general concept - a structure with key-value pairs, but the coersion type should work differently. Map used to restrict keys to a specific type and values ​​for another specific type, but you do not need it. You need something stronger - not only the coersion type for keys and values ​​separately, but also the binding between the key type and value type.

0
source share

I do not think that you mean using only the announcement of the participant. What you can do is hide the map and provide more stringent access methods to the map.

For example, for example:

 class FunctionMapper { private final Map<Class<?>, BiFunction<?, ?, Boolean>> MAP = new HashMap<>(); public <T> void map(Class<T> c, BiFunction<T, T, Boolean> f) { MAP.put(c, f); } public <T> BiFunction<T, T, Boolean> get(Class<T> c) { return (BiFunction<T, T, Boolean>) MAP.get(c); } public <T> Boolean apply(T o1, T o2) { return get((Class<T>) o1.getClass()).apply(o1, o2); } } 

In your equality helper you can use it like this:

  FunctionMapper mapper = new FunctionMapper(); // make this static final mapper.map(String.class, (s1, s2) -> s1.compareTo(s2) == 0); System.out.println(mapper.apply("foo", "bar")); 
0
source share

All Articles