2014-05-13 26 views
5

Non voglio avere un normale lambda che implementa un metodo e ridefinisce il suo toString come valore aggiunto. Voglio che l'espressione lambda implementa solo il metodo toString. So che non sto esprimendo molto bene, ma sono certo che mi capirai con questo esempio.Come eseguire un'espressione lambda per definire toString in Java 8?

public class LambdaToStringTest { 

    public interface ToStringInterface { 
     public abstract String toString(); 
    } 

    public static void main(String[] args) { 
     print("TRACE: %s", (ToStringInterface)()->someComputation()); // <<-- ERROR: The target type of this expression must be a functional interface 
    } 

    private static void print(String format, Object... args) { 
     System.out.println(String.format(format, args)); 
    } 

} 

compila se cambio il nome del metodo, ma poi non sovrascrive toString in modo metodo di stampa non stampa cosa ci si aspetta.

Si tratta di un tentativo di definire un sottosistema di log che valuta lambdas solo quando necessario (quando verrà realmente stampato) ma compatibile con argomenti non lambda. Conosco altri modi per raggiungerlo, ma mi chiedo perché non posso farlo in questo modo e se c'è una soluzione alternativa o sto facendo qualcosa di sbagliato,

+0

Io non chiedo questo: http://stackoverflow.com/questions/16544583/can-i-override-tostring- method-of-functional-interface-in-jdk8-using-lambdas Voglio implementare solo toString. – aalku

risposta

5

Risposta breve, non è possibile. @FunctionalInterface s non può essere utilizzato per "sovrascrivere" i metodi da Object.

È tuttavia possibile implementare Formattable con un metodo di estensione virtuale. Nota: codice qui sotto è il fitness:

@FunctionalInterface 
public interface ToStringInterface 
    extends Formattable 
{ 
    String asString(); 

    @Override 
    default void formatTo(Formatter formatter, int flags, int width, int precision) 
    { 
     formatter.format("%s", this); 
     // Or maybe even better: 
     formatter.out().append(this.asString()); 
    } 
} 

propongo questa soluzione dal momento che si sta utilizzando String.format() che fa uso di questa interfaccia.

Oppure puoi semplicemente definire la tua interfaccia. O anche scrivere un wrapper per questa interfaccia che chiama .toString() in .asString(). Le scelte sono molte.

+0

Non penso che dovresti usare this.toString in un'interfaccia pensata per essere implementata da un lambda in quanto stamperebbe informazioni inutili. – aalku

+1

Beh, è ​​una tua scelta, non posso dire di più;) O forse i lambda non sono adatti per quello che stai cercando di fare. Ad ogni modo, hai la mia visione di una soluzione. – fge

+1

Sembra che tu ti sia confuso. Nell'ultima riga dovrebbe essere 'this.asString()' piuttosto che 'this.toString()' dato che questo è il punto della tua risposta che lambda implementerà 'asString' piuttosto che' toString'. – Holger

2

Come sottolineato da fge, le interfacce non possono dichiarare metodi dalla classe Object (toString, equals, hashCode).

Penso che Holger abbia ragione nel indicarti il ​​Fornitore, e penso che dato il tuo dichiarato scopo di creare un valutatore di log pigro, dovresti semplicemente consegnare il cast all'interno del tuo metodo di stampa. Per aiutare con la sintassi delle chiamate di stampa, è possibile creare un metodo di utilità che esegue essenzialmente il cast per voi:

private static void print(String format, Object... args) { 
    for (int i = 0; i < args.length; i++) { 
     if (args[i] instanceof Supplier) { 
      args[i] = ((Supplier<?>)args[i]).get(); 
     } 
    } 
    System.out.println(String.format(format, args)); 
} 

private static <T> Supplier<T> supply(Supplier<T> supplier) { 
    return supplier; 
} 

private static class Example { 

    private static String someString() { 
     return "hello"; 
    } 

    private static Boolean someBoolean() { 
     return true; 
    } 
} 

public static void main(String[] args) { 

    print("TRACE: %s; %s; %s", 
     supply(Example::someString), 
     supply(Example::someBoolean), 
     "extra"); 
} 
+0

Il metodo dell'incantonamento 'fornitura' è una buona idea, ma potrei voler stampare toString da un oggetto che implementa il fornitore, quindi è meglio usare un'interfaccia personalizzata. – aalku

1

Functionals bisogno di conoscere il loro tipo abbastanza rapidamente, il che significa che avrete un sacco di calchi to ToStringInterface se cerchi di rimanere troppo vicino alla tua idea originale. Invece del cast, puoi chiamare un metodo statico.

static Object asString(Supplier<String> string){ 
    return new Object(){ 
     public String toString(){ 
      return string.get(); 
     } 
    }; 
}    

public static void main(String[] args) { 
    print("TRACE: %s", asString(()->someComputation())); 
} 

Onestamente, però, il commento di Holger è quello che vorrei fare -

void print(String pattern, Supplier<?>... args);