2013-06-17 14 views
5

Questa potrebbe sembrare una cosa strana da volere, ma esiste un modo in Java per impedire alle sottoclassi di aggiungere nuovi metodi (compresi i costruttori) consentendo comunque alle sottoclassi di sovrascrivere i metodi?Prevenire le sottoclassi dall'aggiunta dei metodi

La situazione attuale è dove abbiamo una classe abstract con alcuni metodi astratti e un costruttore

abstract class A { 
    abstract A doX(); 
    abstract boolean isY(); 
    public A(String s){ ... }; 
} 

e vogliamo tutte le sottoclassi concrete di questa classe per eseguire l'override solo questi metodi e costruttore.

Si tratta di applicare un certo stile nel nostro codice, ovvero impedire ad altre persone che lavorano al codice di aggiungere cose. Potremmo semplicemente dirgli di no, ma raramente funziona, quindi ci chiedevamo se esistesse un modo programmatico per raggiungere questo obiettivo.

Ovviamente la classe non può essere final. L'efficienza non è fondamentale: un codice più pulito è più importante.

Update - approccio dinamico

Come è stato sottolineato nelle risposte, non v'è alcun modo per farlo in modo statico, come l'unico modo per evitare di essere sottoclassi create sta usando final, che non funziona. Ma potrei usare un approccio dinamico, quindi la mia soluzione attuale è aggiungere questo aspect al progetto (che già utilizza AspectJ).

public aspect WatchA{ 
     before() : execute(* A.*()) || execute(* A.*(..)) { 
     String methodCalled = joinPoint.getSignature().getName(); 
     Class<?> c = Class.forName(args[0]) 
     Method[] allMethods = c.getDeclaredMethods(); 
     boolean found = false; 
     for(Method m : allMethods) 
      found |= m.getName().equals(methodCalled); 
     if(!found) 
      throw new RuntimeException("Do not add method "+methodCalled+" to A"); 
     } 
    } 

Che causerà il fallimento dei test se utilizzano uno di questi nuovi metodi.

+0

Java non lo consente. È meglio introdurre una rigorosa politica di revisione del codice. –

+0

Per curiosità, perché è vietato l'ultimo? – fge

+2

Voglio che le sottoclassi siano in grado di sovrascrivere i metodi ma non aggiungerli, quindi dobbiamo essere in grado di creare sottoclassi. Capisco che questo è tutto molto sopra-sottoscritto e, ad essere onesto, ora sono principalmente interessato solo se può essere fatto - penso che finiremo per andare con l'approccio "dillo a loro". – selig

risposta

2

Se davvero non si vuole fare questo .. un modo sarebbe quello di verificare in modo programmatico nel costruttore astratto della classe che i metodi definiti nella classe siano quelli consentiti.

import java.lang.reflect.Method; 
import java.util.Arrays; 
import java.util.HashSet; 
import java.util.Set; 

public abstract class Base { 

    private static final Set<String> allowedMethodNames = new HashSet<>(Arrays.asList("doThis", "wait", "wait", "wait", "equals", "toString", "hashCode", "getClass", "notify", "notifyAll")); 

    public Base() { 
     Set<String> allMethods = new HashSet<>(); 
     for (Method aMethod : getClass().getMethods()) { 
      allMethods.add(aMethod.getName()); 
     } 
     if (!allowedMethodNames.equals(allMethods)) { 
      allMethods.removeAll(allowedMethodNames); 
      throw new IllegalStateException("Following methods not allowed <" + allMethods + ">"); 
     } 
    } 

    public abstract void doThis(); 
} 

public class Disallowed extends Base { 

    @Override 
    public void doThis() { 
     System.out.println("dooooooo"); 
    } 

    public void doSomethingElse() { 
     System.out.println("not allowed"); 
    } 

    public static void main(String[] args) { 
      new Allowed().doThis(); 
     new Disallowed(); 
    } 

} 

public class Allowed extends Base { 

    @Override 
    public void doThis() { 
     System.out.println("doing this"); 
    } 


} 

Quando qualcuno tenta di creare un'istanza di "Non consentito", fallirebbe. Comunque 'new Allowed(). DoThis()' funzionerà correttamente.

Un modo più agevole per farlo sarebbe quello di introdurre un'annotazione personalizzata + un processore di annotazione e fare lo stesso controllo durante il tempo di compilazione.

2

Non esiste un modo. Perché vorresti imporre un simile stile di codifica?

Se è necessario applicare uno stile di questo tipo, è possibile creare un "rule enforcer" che verifica il percorso di classe e confronta i metodi delle classi parent astratte con le relative classi secondarie.

+2

Per favore aggiungilo come commento. –

8

Non puoi farlo. Solo se le classi sono final puoi assicurarti che non sia possibile creare alcuna sottoclasse.

È anche possibile definire i metodi final (anche in classi astratte) in modo che la loro esclusione sia proibita.

La soluzione migliore è creare un'interfaccia, con tutti i metodi che si desidera visualizzare e forzare tutti gli utenti della propria API ad accedere agli oggetti tramite questa interfaccia. In questo modo, anche se le implementazioni aggiungono le proprie cose, le cose dette non saranno visibili.

Una soluzione è quella di implementare una fabbrica per restituire le classi concrete; per "maggiore sicurezza", si potrebbe mettere tutte le implementazioni nello stesso pacchetto come questa fabbrica e rendere il pacchetto costruttori locale (ma questo spesso non è pratico):

public final class MyFactory 
{ 
    // .... 
    public MyInterface getConcrete() 
    { 
     return new MyInterfaceImpl(); 
    } 
    // etc etc -- getStones(), getTar(), getFeathers() and so on 
} 

Nota che i costruttori possono essere utilizzati anche per questo.

+0

Come è questa una risposta a "fermare le sottoclassi dall'aggiunta di nuovi metodi"? inoltre l'OP non vuole impedire alle sottoclassi di sovrascrivere i metodi principali. Vuole che non aggiungano i propri metodi oltre a quelli definiti in parent. Fan fan di Java ogni dove –

+3

@srikanthyaradla Si tratta di "interrompere le sottoclassi dall'aggiunta di nuovi metodi" - le prime 4 parole: "Non puoi farlo". E poi propongo un'alternativa. Qual è il problema? – fge

+1

Avresti potuto semplicemente fermarti lì. Ma hai detto "Una soluzione per questo è implementare una fabbrica per restituire le classi concrete" che non impedisce alle classi di implementazione di aggiungere i propri metodi. –

1

È Java che significa flessibilità. Quindi java ti dà più convenienza nell'usare i metodi astratti e sovrascriverli dalle sottoclassi. Inoltre si dovrebbe avere un'idea di aggiungere nuovi metodi a queste sottoclassi. Anche Java non può cambiarlo. Se fa allora l'intero crash della comunità Java. È impossibile evitare di aggiungere metodi alle loro sottoclassi. Solo tu puoi fermarli estendendo le tue lezioni e sovrascrivendo i tuoi metodi.

Problemi correlati