2016-03-03 19 views
5

Ecco un esempio semplificato che mostra il mio problema:Deve implementare il metodo di interfaccia predefinito?

import java.util.List; 

public interface SingleTask extends List<Runnable>, Runnable { 
    default Runnable get(final int x) { 
     if (x != 0) { 
      throw new IndexOutOfBoundsException(); 
     } 
     return this; 
    } 

    default int size() { 
     return 1; 
    } 
} 

import java.util.AbstractList; 

public class MyTask extends AbstractList<Runnable> implements SingleTask { 
    @Override 
    public void run() { 
     System.out.println("hello"); 
    } 
} 

In SingleTask I fornire implementazioni per i metodi get e size, che sono gli unici metodi astratti da AbstractList. Tuttavia, quando compilo MyTask, ho ancora ottenere errori come:

The type MyTask must implement the inherited abstract method AbstractCollection.size()

o

MyTask.java:3: error: MyTask is not abstract and does not override abstract method get(int) in AbstractList

(a seconda del compilatore). Sono, ovviamente, utilizzando Java 8.

Così ho due domande:

  1. Perché ricevo questi errori? Mi aspettavo che riconoscesse le implementazioni predefinite.
  2. Se non dovrebbe funzionare in questo modo, qual è il modo più semplice per utilizzare questi due metodi in MyTask senza copiare l'intero codice?
+1

Non prolungare le attività 'Elenco ' (cosa vuol dire semanticamente lo stesso?), Invece creare un'interfaccia 'Task' con un' Elenco pubblico getRunnables(); 'metodo. In caso contrario, è sufficiente rendere 'SingleTask' una classe astratta anziché un'interfaccia. – biziclop

+1

Vale la pena notare che Eclipse sta soffocando su 'size()' non implementato: questo è probabilmente un bug di Eclipse. 'javac' 1.8.0_51 sta soffocando su' get (int) 'non viene implementato ed è giusto: non è implementato. – Tunaki

+0

@biziclop dovrebbe essere una lista di compiti; SingleTask è un'implementazione singleton – aditsu

risposta

6

Forzare SingleTask implementatori di implementare anche tutti i metodi di List non è molto elegante, e metodi predefiniti non sono destinati ad essere utilizzati per definire le entità trait-simili, che l'interfaccia SingleTask assomiglia.

Ci sono diversi motivi per cui i metodi predefiniti-come-tratti sono una cattiva idea, il più ovvio è che qualsiasi implementatore può semplicemente sovrascrivere il metodo predefinito, rovinando la propria caratteristica.

e questo è esattamente ciò che sta accadendo qui: dal AbstractList dichiara esplicitamente get() e size() come abstract, significa SingleTask li erediterà, piuttosto che le implementazioni di default si può avere avuto in un superinterfaccia.

JLS 8.4.8:

A class C inherits from its direct superclass and direct superinterfaces all abstract and default (§9.4) methods m for which all of the following are true:

...

  • No concrete method inherited by C from its direct superclass has a signature that is a subsignature of the signature of m.

Tenendo tutto questo in mente la soluzione più semplice è probabilmente questo:

public abstract class SingleTask extends AbstractList<Runnable> implements Runnable { 
    @Override 
    public final Runnable get(final int x) { 
     if (x != 0) { 
      throw new IndexOutOfBoundsException(); 
     } 
     return this; 
    } 

    @Override 
    public final int size() { 
     return 1; 
    } 

    @Override 
    public abstract void run(); 
} 

Il suo svantaggio è che le attività devono estendersi SingleTask e quindi non può estendere qualsiasi altra cosa, per quanto riguarda i lati positivi, anche se non hanno bisogno di affrontare l'attività essendo anche un List, hanno solo bisogno di implementare run().

A lungo termine, preferirei la composizione sull'ereditarietà e le attività semplicemente restituendo un elenco di caratteri eseguibili anziché se stessi.

+0

Non stavo forzando gli implementatori di SingleTask a estendere anche AbstractList, ma sembra che tu sia. Inoltre, hai reso SingleTask una classe, il che significa che non posso estendere una classe diversa se voglio usarla. E non hai nemmeno tentato di rispondere alla mia prima domanda. – aditsu

+1

@aditsu 'Non stavo forzando gli implementer di SingleTask a estendere anche AbstractList' Sì, lo fai, ed è quello che sta causando i problemi. Modificherò la mia risposta per coprire anche questo. – biziclop

+0

Sbagliato, con il mio codice, gli implementatori SingleTask devono solo implementare i metodi di Lista. Non c'è nulla in SingleTask su AbstractList! – aditsu

2
  1. Why am I getting these errors? I was expecting it to recognize the default implementations.

Penso che @biziclop abbia coperto correttamente quello in his answer. In breve, come AbstractList dichiara come astratti i metodi get(int) e size(), questi hanno la precedenza sulle implementazioni predefinite in SingleTask.

  1. If it's not supposed to work like that, then what's the simplest way to use those two methods in MyTask without copying the whole code?

Il modo più semplice sarebbe quella di ignorare get(int) e size() metodi MyTask, in modo che essi delegano ai vostri metodi predefiniti in SingleTask interfaccia:

public class MyTask extends AbstractList<Runnable> implements SingleTask { 

    @Override 
    public void run() { 
     System.out.println("hello"); 
    } 

    @Override 
    public Runnable get(int index) { 
     return SingleTask.super.get(index); 
    } 

    @Override 
    public int size() { 
     return SingleTask.super.size(); 
    } 
} 

Con questo approccio, si sarebbe tipo di delegante con i metodi predefiniti in SingleTask. Non penso che questa sia una brutta cosa (almeno, non è necessario usare un attributo). Inoltre, ha senso scrivere questi metodi, in modo che tu possa scegliere quale interfaccia fornisca le implementazioni predefinite.