2012-11-08 10 views
12

Ecco il mio problema in realtà sto affrontando ora. Ho una classe, diciamo Foo e questa classe definisce un metodo chiamato getBar che restituisce un'istanza Bar. La classe Bar è definita all'interno di Foo ed è dichiarata public static final. Quello che voglio fare è definire una classe MyFoo che si estende Foo ma voglio anche estendere Bar con MyBar aggiungendo le mie funzionalità (metodi, proprietà, ecc.). Voglio anche getBar restituire MyBar.Come estendere una classe finale in Java

Il problema è Bar è definitivo. Ecco un esempio di quello che voglio fare:

public class Foo { 

    Bar bar = new Bar(); 

    public Bar getBar(){ 
     return bar; 
    } 

    .... 

    public static final class Bar { 

    } 
} 

Quello che voglio fare è:

public class MyFoo extends Foo { 


    public MyBar getBar(){ // here I want to return an instance of MyBar 

    } 

    public static final class MyBar extends Bar { // That's impossible since Bar is final 

    } 
} 

Come posso raggiungere questo obiettivo?

[EDIT] Sto aggiungendo ulteriori dettagli alla mia domanda. Attualmente sto sviluppando un plugin per Jenkins e dopo la ricerca, mi rendo conto che esiste un plug-in che offre le funzionalità di base che voglio fare, Foo e Bar. Voglio personalizzare Foo e Bar, in modo che possa soddisfare le mie esigenze. Foo è la classe principale del plugin e estende una classe chiamata Builder. Foo definisce un metodo chiamato perform che contiene tutte le operazioni per eseguire una compilazione. Bar contiene operazioni per scopi di configurazione e Foo ha bisogno di Bar per ottenere quelle informazioni.

Quindi quello che voglio fare è estendere Foo con MyFoo e aggiungere alcune configurazioni con MyBar. E come vedete, MyFoo sarà necessario ottenere quei configuations chiamando getBar();

La seconda cosa è che non ho il controllo della instanciation di MyFoo e MyBar

quello che mi blocchi, è che non riesco a estendere Bar. Come posso ottenerlo?

+8

Dato che il modificatore di 'final' impedisce di sottoclasse' Bar', mi consiglia di "avvolgerlo". – mre

+0

Se 'Bar' è una' classe finale', di solito è per una buona ragione, ad es. forse perché alcuni invarianti di 'Foo' lo richiedono. Quindi non lo farò, o almeno dovresti capire esattamente tutto il codice sorgente pertinente, e magari riscriverlo. –

+2

Chiedi allo sviluppatore di 'Bar' di renderlo non-finale o usa la delega come suggerisce mre. –

risposta

26

Non è possibile, in sostanza. Sembra che lo sviluppatore che ha progettato lo Bar abbia deciso di non estenderlo, quindi non è possibile estenderlo. Avrai bisogno di cercare un design diverso - possibilmente uno che non usi l'eredità così tanto ... presumendo che tu non possa cambiare il codice esistente, naturalmente.

(E 'difficile dare ulteriori consigli concreti senza sapere di più sul vero obiettivo qui -. La progettazione del sistema nel suo complesso)

+1

Beh, in realtà sto sviluppando un plugin per Jenkins e il fatto è che Foo and Bar ha molte funzionalità che voglio estendere. Questo mi impedirebbe di riscrivere lo stesso codice di Foo e Bar – Dimitri

+1

@Dimitri: Questo in realtà non ci fornisce abbastanza informazioni per sapere se sarebbe ragionevole usare la composizione invece dell'ereditarietà, per esempio. –

+0

'Foo' e' Bar' sono stati progettati deliberatamente in modo da non poterli estendere. Non hai davvero un'alternativa. –

4

Non è possibile estendere le classi finali, che è il punto di dichiarare classi finale. È possibile definire un'interfaccia ereditata dalla classe finale e una classe wrapper che deleghi le chiamate alla classe finale spostata. La domanda è quindi perché rendere la finale di classe al primo posto.

6

Non è possibile estendere una classe dichiarata final. Tuttavia è possibile creare una classe intermedia, denominata Wrapper. Questo Wrapper contiene fondamentalmente un'istanza della classe originale e fornisce metodi alternativi per modificare lo stato dell'oggetto in base a ciò che si desidera fare.

Naturalmente questo non darà l'accesso alle & campi privati ​​protetti della classe final, ma è possibile utilizzare le sue getter e metodi setter, in combinazione con i nuovi metodi & campi dichiarati nella classe Wrapper per ottenere il risultato che voglio, o qualcosa di relativamente vicino.

Un'altra soluzione non è dichiarare final una classe se non esiste un motivo valido per farlo.

5

Non è possibile estendere una classe final: questo è lo scopo della parola chiave final.

Tuttavia, ci sono (almeno) due modi di aggirare:

  1. La classe è nel progetto Jenkins, che è open source, quindi si può semplicemente scaricare il progetto, apportare le modifiche desiderate la classe e ri-costruire il vaso
  2. un po 'di un hack, ma si potrebbe usare una libreria come Javassist per sostituire l'implementazione della classe in memoria
+0

Perché qualcuno dovrebbe fare una finale di classe? Può sembrare una domanda terribile, ma sono nuovo nella programmazione. Stavo cercando di estendere la classe Scanner (solo per vedere se potevo) e ho visto che non avrebbe funzionato.Il mio amico mi ha detto che era perché la classe Scanner era definitiva. Diciamo che teoricamente non era definitivo e qualcuno lo ha esteso. Quale sarebbe il problema dal momento che qualsiasi variabile che viene modificata all'interno della classe figlio non deve modificare la classe genitore? Ecc. Idk –

+0

So che ha qualcosa a che fare con la classe Scanner che viene sovrascritta, ma non saprei nemmeno come sovrascriverla se non fosse definitiva e ho provato. –

0

Per esempio, non si può aggiungere uno StringBuffer nella collezione TreeSet, perché TreeSet non è figlio del Interfaccia Comparable:

public class Main(){ 
public static void main(String[] args){ 
    TreeSet treeSetSB = new TreeSet(); 
    treeSetSB.add(new StringBuffer("A")); //error, Comparable is not implemented 
} 

ma possiamo utilizzare un wrapper per l'attuazione del Comparabile:

class myWrap implements Comparable{ 
    TreeSet treeset; 
    public myWrap() { 
     this.treeset=new TreeSet<>(); 

    } 
    public TreeSet getTreeset() { 
     return treeset; 
    } 
    public void setTreeset(TreeSet treeset) { 
     this.treeset = treeset; 
    } 
} 


public class Main(){ 
public static void main(String[] args){ 

    myWrap myWrap =new myWrap(); 
    TreeSet myTrs =myWrap.getTreeset(); 
    myTrs.add("b"); // no error anymore 
    myTrs.add("a"); 
    myTrs.add("A"); 
    System.out.println(myTrs); 
}