Error in a system like Java?

Imagine that you have a main class - a simulator - which uses two other classes - Manufacturers and Appraisers , which implement the interfaces IProducer and IEvaluator, respectively.

IProducer implementations yield results, while IEvaluator implementations evaluate these results. The simulator controls the flow of work by requesting an implementation of IProducer and passing the results to an instance of IEvaluator.

The actual implementation of the Producer and Appraiser is known at runtime, at compile time I only know their interfaces. See an example below.

package com.test; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; /** * Producers produce results. I do not care what their actual type is, but the * values in the map have to be comparable amongst themselves. */ interface IProducer<T extends Comparable<T>> { public Map<Integer, T> getResults(); } /** * This example implementation ranks items in the map by using Strings. */ class ProducerA implements IProducer<String> { @Override public Map<Integer, String> getResults() { Map<Integer, String> result = new HashMap<Integer, String>(); result.put(1, "A"); result.put(2, "B"); result.put(3, "B"); return result; } } /** * This example implementation ranks items in the map by using integers. */ class ProducerB implements IProducer<Integer> { @Override public Map<Integer, Integer> getResults() { Map<Integer, Integer> result = new HashMap<Integer, Integer>(); result.put(1, 10); result.put(2, 30); result.put(3, 30); return result; } } /** * Evaluator evaluates the results against the given groundTruth. All it needs * to know about results, is that they are comparable amongst themselves. */ interface IEvaluator { public <T extends Comparable<T>> double evaluate(Map<Integer, T> results, Map<Integer, Double> groundTruth); } /** * This is example of an evaluator, metric Kendall Tau-B. Don't bother with * semantics, all that matters is that I want to be able to call * r1.compareTo(r2) for every (r1, r2) that appear in Map<Integer, T> results. */ class KendallTauB implements IEvaluator { @Override public <T extends Comparable<T>> double evaluate(Map<Integer, T> results, Map<Integer, Double> groundTruth) { int concordant = 0, discordant = 0, tiedRanks = 0, tiedCapabilities = 0; for (Entry<Integer, T> rank1 : results.entrySet()) { for (Entry<Integer, T> rank2 : results.entrySet()) { if (rank1.getKey() < rank2.getKey()) { final T r1 = rank1.getValue(); final T r2 = rank2.getValue(); final Double c1 = groundTruth.get(rank1.getKey()); final Double c2 = groundTruth.get(rank2.getKey()); final int ranksDiff = r1.compareTo(r2); final int actualDiff = c1.compareTo(c2); if (ranksDiff * actualDiff > 0) { concordant++; } else if (ranksDiff * actualDiff < 0) { discordant++; } else { if (ranksDiff == 0) tiedRanks++; if (actualDiff == 0) tiedCapabilities++; } } } } final double n = results.size() * (results.size() - 1d) / 2d; return (concordant - discordant) / Math.sqrt((n - tiedRanks) * (n - tiedCapabilities)); } } /** * The simulator class that queries the producer and them conveys results to the * evaluator. */ public class Simulator { public static void main(String[] args) { // example of a ground truth Map<Integer, Double> groundTruth = new HashMap<Integer, Double>(); groundTruth.put(1, 1d); groundTruth.put(2, 2d); groundTruth.put(3, 3d); // dynamically load producers List<IProducer<?>> producerImplementations = lookUpProducers(); // dynamically load evaluators List<IEvaluator> evaluatorImplementations = lookUpEvaluators(); // pick a producer IProducer<?> producer = producerImplementations.get(0); // pick an evaluator IEvaluator evaluator = evaluatorImplementations.get(0); // evaluate the result against the ground truth double score = evaluator.evaluate(producer.getResults(), groundTruth); System.out.printf("Score is %.2f\n", score); } // Methods below are for demonstration purposes only. I'm actually using // ServiceLoader.load(Clazz) to dynamically discover and load classes that // implement interfaces IProducer and IEvaluator public static List<IProducer<?>> lookUpProducers() { List<IProducer<?>> producers = new ArrayList<IProducer<?>>(); producers.add(new ProducerA()); producers.add(new ProducerB()); return producers; } public static List<IEvaluator> lookUpEvaluators() { List<IEvaluator> evaluators = new ArrayList<IEvaluator>(); evaluators.add(new KendallTauB()); return evaluators; } } 

This code compiles without warning, and also works as it should. This is the solution to the question I asked before , so this is kind of the next question.

Using the code above, imagine that you want to save the result of a call to manufacturer.getResults () in a variable (which will later be used in a call to the calculateator.evaluate (results, groundTruth) method). What will be the type of this variable?

Map <Integer ,? >, Map <Integer ,? extends Comparable <โ†’? Make the main method common and use a common type? Nothing I've tried so far works. The compiler complains about every type I came up with.

 public static void main(String[] args) { // example of a ground truth Map<Integer, Double> groundTruth = new HashMap<Integer, Double>(); groundTruth.put(1, 1d); groundTruth.put(2, 2d); groundTruth.put(3, 3d); // dynamically load producers List<IProducer<?>> producerImplementations = lookUpProducers(); // dynamically load evaluators List<IEvaluator> evaluatorImplementations = lookUpEvaluators(); // pick a producer IProducer<?> producer = producerImplementations.get(0); // pick an evaluator IEvaluator evaluator = evaluatorImplementations.get(0); // evaluate the result against the ground truth Map<Integer, ?> data = producer.getResults(); // this type works double score = evaluator.evaluate(data, groundTruth); // but now this call does not System.out.printf("Score is %.2f\n", score); } 

It seems that the producer .getResults () returns something that cannot be statically expressed in Java. Is this a mistake, or am I missing something?

+4
source share
2 answers

This is not a mistake, but really a limitation. In the system type community, it is well known that wildcard Java has types that cannot be expressed in Java syntax. Your example demonstrates one such case and demonstrates that wildcards are essentially incompatible with F-constrained polymorphism (i.e., enter the form parameters T extends Something<T> ).

Frankly speaking, wildcards are a terrible type of hacking system. They should never be introduced in Java. What you really need and to make your example expressive are the right existential types (of which wildcards are a limited ad-hoc option). Unfortunately, Java does not have them (although Scala does).

+1
source

One note before my answer: all your T extends Comparable<T> statements should probably be T extends Comparable<? super T> T extends Comparable<? super T> , it gives more flexibility (why do you need it if it compares T or Object s?), and it is necessary for my solution to work.

This is actually not a โ€œmistakeโ€ in the Java type system, it is simply an inconvenience. Java does not particularly like to have intersection types as part of a type declaration.

One way I found to get around this is to create a โ€œunsafeโ€ method that should never be used under normal circumstances:

 @SuppressWarnings("unchecked") private static <T extends Comparable<? super T>> Map<Integer, T> cast(Map<Integer, ?> map) { return (Map<Integer, T>) map; } 

Just make sure you call this method with Map , which is actually Map<Integer, T extends Comparable<? super T>> Map<Integer, T extends Comparable<? super T>> (for example, those that IProducer return).

Using this method, you can do the following:

 IProducer<?> producer = ... IEvaluator evaluator = ... Map<Integer, ?> product = producer.getResults(); evaluator.evaluate(cast(product), truth); 

And then Java will automatically execute the correct type parameter for you.

Also, the prefix I usually not favored by the Java community.

+2
source

Source: https://habr.com/ru/post/1432167/


All Articles