2012-11-08 15 views
12

Oto mój problem, w obliczu którego stoję teraz. Mam klasę, powiedzmy Foo i ta klasa definiuje metodę o nazwie getBar, która zwraca instancję Bar. Klasa Bar jest zdefiniowana wewnątrz Foo i jest zadeklarowana jako public static final. Chcę tylko zdefiniować klasę MyFoo, która rozszerza się o Foo, ale chcę również rozszerzyć Bar o MyBar przez dodanie własnych funkcjonalności (metod, właściwości itp.). Chcę również getBar, aby zwrócić MyBar.Jak przedłużyć ostatnią klasę w Javie

Problem jest kończący się Bar. Oto ilustracja tego, co chcę zrobić:

public class Foo { 

    Bar bar = new Bar(); 

    public Bar getBar(){ 
     return bar; 
    } 

    .... 

    public static final class Bar { 

    } 
} 

Co chcę zrobić, to:

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 

    } 
} 

Jak mogę to osiągnąć?

[EDIT] Dodaję więcej szczegółów do mojego pytania. Aktualnie tworzę wtyczkę dla Jenkinsa i po przeszukaniu zdaję sobie sprawę, że istnieje wtyczka oferująca podstawowe funkcje, które chcę wykonać, Foo i Bar. Chcę niestandardowe Foo i Bar, więc może pasować do moich potrzeb. Foo jest główną klasą wtyczki i rozszerza klasę o nazwie Builder. Foo definiuje metodę o nazwie perform, która zawiera wszystkie operacje służące do wykonania kompilacji. Bar zawiera operacje dla celów konfiguracyjnych, a Foo potrzebuje paska, aby uzyskać te informacje.

Więc chcę rozszerzyć Foo o MyFoo i dodać kilka konfiguracji z MyBar. I jak widać, MyFoo będą musiały uzyskać te configuations wywołując getBar();

Drugą rzeczą jest to, że nie mają kontroli nad instanciation z MyFoo i MyBar

, co mnie bloków, jest to, że nie mogę przedłużyć Bar. Jak mogę to osiągnąć?

+8

Ponieważ modyfikator 'final' zapobiega podklasowaniu' Bar', polecam "zawijanie" go. – mre

+0

Jeśli 'Bar' jest' ostateczną klasą' zwykle jest z jakiegoś dobrego powodu, np. może dlatego, że wymagają tego niektóre niezmienniki 'Foo'. Więc nie zrobię tego, a przynajmniej powinieneś dokładnie zrozumieć cały odpowiedni kod źródłowy, a być może przepisać go ponownie. –

+2

Zapytaj programistę 'Bar', aby uczynił go nieostatnim lub skorzystaj z delegacji, jak sugeruje mre. –

Odpowiedz

26

Zasadniczo nie możesz. Wygląda na to, że programista, który zaprojektował Bar, nie został przedłużony, więc nie możesz go rozszerzyć. Będziesz musiał poszukać innego wzoru - być może takiego, który nie dziedziczy tyle dziedziczenia ... zakładając, że oczywiście nie możesz zmienić istniejącego kodu.

(Trudno podać bardziej konkretne porady, nie wiedząc więcej o prawdziwym celem tutaj. - projektowania całego systemu)

+1

Cóż, faktycznie rozwijam wtyczkę dla Jenkinsa i rzecz jest Foo, a Bar ma wiele funkcji, które chcę rozszerzyć. To uniemożliwiłoby mi przepisanie tego samego kodu Foo i Bara – Dimitri

+1

@Dimitri: To naprawdę nie daje nam wystarczających informacji, aby wiedzieć, czy rozsądne byłoby na przykład użycie kompozycji zamiast dziedziczenia. –

+0

'Foo' i' Bar' zostały zaprojektowane celowo, więc nie można ich rozszerzyć. Tak naprawdę nie masz alternatywy. –

4

Nie można przedłużyć klas końcowych, które jest punktem deklarowania klas ostateczna. Można zdefiniować interfejs, z którego dziedziczy się zarówno ostatnia klasa, jak i klasa opakowania, która deleguje wywołania do opakowanej klasy końcowej. Nasuwa się więc pytanie, dlaczego na pierwszym miejscu powinna być klasa.

6

Nie można rozszerzyć klasy o nazwie final. Można jednak utworzyć klasę pośrednią o nazwie Wrapper. Ten Wrapper będzie zawierał instancję oryginalnej klasy i zapewnia alternatywne metody modyfikacji stanu obiektu zgodnie z tym, co chcesz zrobić.

Oczywiście tego nie da dostęp do prywatnych & chronionych obszarach klasy final, ale można korzystać z jego pobierające i metody ustawiające w połączeniu z nowymi metodami & pola ty zadeklarowane w klasie Wrapper, aby uzyskać wynik, który chcesz lub coś stosunkowo blisko.

Innym rozwiązaniem nie jest deklarowanie klasy final, jeśli nie ma do tego uzasadnienia.

5

Nie można rozszerzyć klasy final - taki jest cel słowa kluczowego final.

Jednakże istnieją (co najmniej) dwóch obejść:

  1. Klasa jest w projekcie Jenkins, który jest open source, więc można po prostu pobrać projekt wprowadzić zmiany, które chcesz klasa i ponownie zbudować słoik
  2. Nieco hack, ale można korzystać z biblioteki jak Javassist wymienić implementację klasy w pamięci
+0

Dlaczego ktoś miałby dokonać ostatecznej klasy? To może brzmieć jak straszne pytanie, ale jestem nowy w programowaniu. Próbowałem rozszerzyć klasę Scanner (tylko po to, aby zobaczyć czy mógłbym) i zobaczyłem, że to nie zadziała.Mój przyjaciel powiedział mi, że to dlatego, że klasa Scanner była ostateczna. Powiedzmy, że teoretycznie nie była ona ostateczna i ktoś ją rozszerzył. Jaki byłby problem, ponieważ zmienne, które zostały zmienione w klasie podrzędnej, nie muszą zmieniać klasy nadrzędnej? Itd. Idk –

+0

Wiem, że ma to coś wspólnego z przesłonięciem klasy Scanner, ale nie wiedziałbym nawet, jak ją zastąpić, jeśli nie była ona ostateczna i próbowałem. –

0

Na przykład nie możemy dodać StringBuffer w TreeSet Collection, ponieważ TreeSet nie jest dzieckiem interfejs Porównywalne:

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

Ale możemy użyć otoki do wdrożenia Porównywalne:

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