Wie parametriere ich eine vergleichbare Schnittstelle?

Ich habe eine Hauptklasse -Simulator - das nutzt zwei andere Klassen -Hersteller undBewerter. Der Produzent erstellt Ergebnisse, während der Evaluator diese Ergebnisse bewertet. Der Simulator steuert den Ausführungsfluss, indem er den Produzenten abfragt und die Ergebnisse an den Evaluator weiterleitet.

Die tatsächliche Implementierung von Producer und Evaluator ist zur Laufzeit bekannt, zur Kompilierungszeit sind mir nur die Schnittstellen bekannt. Unten füge ich den Inhalt von Interfaces, Beispielimplementierungen und der Simulator-Klasse ein.

Alter Code
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 is their type, but the values
 * in the map have to be comparable amongst themselves.
 */
interface IProducer {
    public Map<Integer, Comparable> getResults();
}

/**
 * This implementation ranks items in the map by using Strings.
 */
class ProducerA implements IProducer {
    @Override
    public Map<Integer, Comparable> getResults() {
        Map<Integer, Comparable> result = new HashMap<Integer, Comparable>();
        result.put(1, "A");
        result.put(2, "B");
        result.put(3, "B");
        return result;
    }
}

/**
 * This implementation ranks items in the map by using integers.
 */
class ProducerB implements IProducer {
    @Override
    public Map<Integer, Comparable> getResults() {
        Map<Integer, Comparable> result = new HashMap<Integer, Comparable>();
        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 double evaluate(Map<Integer, Comparable> results,
            Map<Integer, Double> groundTruth);
}

/**
 * This is example of an evaluator (a metric) -- Kendall's Tau B.
 */
class KendallTauB implements IEvaluator {
    @Override
    public double evaluate(Map<Integer, Comparable> results,
            Map<Integer, Double> groundTruth) {

        int concordant = 0, discordant = 0, tiedRanks = 0, tiedCapabilities = 0;

        for (Entry<Integer, Comparable> rank1 : results.entrySet()) {
            for (Entry<Integer, Comparable> rank2 : results.entrySet()) {
                if (rank1.getKey() < rank2.getKey()) {
                    final Comparable r1 = rank1.getValue();
                    final Comparable r2 = rank2.getValue();
                    final Double c1 = groundTruth.get(rank1.getKey());
                    final Double c2 = groundTruth.get(rank2.getKey());

                    final int rankDiff = r1.compareTo(r2);
                    final int capDiff = c1.compareTo(c2);

                    if (rankDiff * capDiff > 0) {
                        concordant++;
                    } else if (rankDiff * capDiff < 0) {
                        discordant++;
                    } else {
                        if (rankDiff == 0)
                            tiedRanks++;

                        if (capDiff == 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) {
        Map<Integer, Double> groundTruth = new HashMap<Integer, Double>();
        groundTruth.put(1, 1d);
        groundTruth.put(2, 2d);
        groundTruth.put(3, 3d);

        List<IProducer> producerImplementations = lookUpProducers();
        List<IEvaluator> evaluatorImplementations = lookUpEvaluators();

        IProducer producer = producerImplementations.get(1); // pick a producer
        IEvaluator evaluator = evaluatorImplementations.get(0); // pick an evaluator
        // Notice that this class should NOT know what actually comes from
        // producers (besides that is comparable)
        Map<Integer, Comparable> results = producer.getResults();
        double score = evaluator.evaluate(results, 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 these interfaces

    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;
    }
}

Dieser Code sollte kompiliert und ausgeführt werden. Sie sollten das gleiche Ergebnis (0,82) erhalten, unabhängig davon, welche Producer-Implementierung Sie auswählen.

Der Compiler warnt mich davor, Generika an mehreren Stellen nicht zu verwenden:

In der Simulator-Klasse, in den Schnittstellen IEvaluator und IProducer sowie in Klassen, die die IProducer-Schnittstelle implementieren, wird bei jedem Verweis auf die Comparable-Schnittstelle die folgende Warnung angezeigt:Vergleichbar ist ein Rohtyp. Verweise auf den generischen Typ Comparable sollten parametrisiert werdenIn Klassen, die IEvaluator implementieren, wird die folgende Warnung angezeigt (beim Aufrufen von compareTo () für die Werte von Map):Typensicherheit: Die Methode compareTo (Object) gehört zum Rohtyp Comparable. Verweise auf den generischen Typ Comparable sollten parametrisiert werden

Trotzdem funktioniert der Simulator. Jetzt möchte ich Kompilierungswarnungen loswerden. Das Problem ist, dass ich keine Ahnung habe, wie die Schnittstellen IEvaluator und IProducer parametriert und wie die Implementierungen von IProducer und IEvaluator geändert werden.

Ich habe einige Einschränkungen:

Ich kann die Art der Werte in der Map nicht kennen, die der Produzent zurückgibt. Aber ich weiß, dass sie alle vom selben Typ sein werden und dass sie die Comparable-Schnittstelle implementieren werden.Ebenso muss die IEvaluator-Instanz nichts über die auszuwertenden Ergebnisse wissen, außer dass sie vom selben Typ sind und vergleichbar sind (IEvaluator-Implementierungen müssen in der Lage sein, die compareTo () -Methode aufzurufen.).Ich muss die Simulator-Klasse aus diesem "vergleichbaren" Dilemma heraushalten - sie muss nichts über diese Typen wissen (abgesehen davon, dass sie vom selben Typ sind, der auch vergleichbar ist). Ihre Aufgabe ist es, dem Evaluator einfach die Ergebnisse vom Produzenten zu übermitteln.

Irgendwelche Ideen?

Überarbeitete und überarbeitete Version

Unter Verwendung einiger Ideen aus den nachstehenden Antworten gelangte ich zu dieser Stufe, die ohne Warnungen kompiliert und ausgeführt wird, ohne dass die SuppressWarnings-Direktive verwendet werden muss. Dies ist sehr ähnlich zu dem, was Eero vorgeschlagen hat, aber die Hauptmethode ist etwas anders.

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 is their type, but the values
 * in the map have to be comparable amongst themselves.
 */
interface IProducer<T extends Comparable<T>> {
    public Map<Integer, T> getResults();
}

/**
 * This 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 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 (a metric) -- Kendall's Tau B.
 */
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 rankDiff = r1.compareTo(r2);
                    final int capDiff = c1.compareTo(c2);

                    if (rankDiff * capDiff > 0) {
                        concordant++;
                    } else if (rankDiff * capDiff < 0) {
                        discordant++;
                    } else {
                        if (rankDiff == 0)
                            tiedRanks++;

                        if (capDiff == 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 Main {
    public static void main(String[] args) {
        Map<Integer, Double> groundTruth = new HashMap<Integer, Double>();
        groundTruth.put(1, 1d);
        groundTruth.put(2, 2d);
        groundTruth.put(3, 3d);

        List<IProducer<?>> producerImplementations = lookUpProducers();
        List<IEvaluator> evaluatorImplementations = lookUpEvaluators();

        IProducer<?> producer = producerImplementations.get(0);
        IEvaluator evaluator = evaluatorImplementations.get(0);

        // Notice that this class should NOT know what actually comes from
        // producers (besides that is comparable)
        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 these interfaces
    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;
    }
}

Der Hauptunterschied scheint in der Hauptmethode zu liegen, die derzeit so aussieht.

    public static void main(String[] args) {
        Map<Integer, Double> groundTruth = new HashMap<Integer, Double>();
        groundTruth.put(1, 1d);
        groundTruth.put(2, 2d);
        groundTruth.put(3, 3d);

        List<IProducer<?>> producerImplementations = lookUpProducers();
        List<IEvaluator> evaluatorImplementations = lookUpEvaluators();

        IProducer<?> producer = producerImplementations.get(0);
        IEvaluator evaluator = evaluatorImplementations.get(0);

        // Notice that this class should NOT know what actually comes from
        // producers (besides that is comparable)
        double score = evaluator.evaluate(producer.getResults(), groundTruth);

        System.out.printf("Score is %.2f\n", score);
    }

Das funktioniert. Wenn ich jedoch den Code in diesen ändere:

    public static void main(String[] args) {
        Map<Integer, Double> groundTruth = new HashMap<Integer, Double>();
        groundTruth.put(1, 1d);
        groundTruth.put(2, 2d);
        groundTruth.put(3, 3d);

        List<IProducer<?>> producerImplementations = lookUpProducers();
        List<IEvaluator> evaluatorImplementations = lookUpEvaluators();

        IProducer<?> producer = producerImplementations.get(0);
        IEvaluator evaluator = evaluatorImplementations.get(0);

        // Notice that this class should NOT know what actually comes from
        // producers (besides that is comparable)

        // Lines below changed
        Map<Integer, ? extends Comparable<?>> ranks = producer.getResults();            
        double score = evaluator.evaluate(ranks, groundTruth);

        System.out.printf("Score is %.2f\n", score);
}

Das verdammte Ding wird nicht einmal kompilieren und sagen:Gebundene Nichtübereinstimmung: Die generische Methode evaluation (Map, Map) vom Typ IEvaluator gilt nicht für die Argumente (Map>, Map). Der abgeleitete Typ capture # 3-of? extend Comparable ist kein gültiger Ersatz für den begrenzten Parameter>

Das ist total komisch für mich. Der Code funktioniert, wenn ich evaluator.evaluate (producer.getResults (), groundTruth) aufrufe. Wenn ich jedoch zuerst die producer.getResults () -Methode aufrufe und in einer Variablen speichere und dann die Evaluierungsmethode mit dieser Variablen aufrufe (evaluator.evaluate (ranks, groundTruth)), erhalte ich den Kompilierungsfehler (unabhängig von der Variablen) Art).

Antworten auf die Frage(3)

Ihre Antwort auf die Frage