2016-06-07 11 views
20

Czy istnieje jakiś dobry sposób użycia try-for-resources podczas otwierania w konstruktorze obiektu InputStream, a następnie przekazywania go super konstruktorowi?Try-with-resources podczas wywoływania super konstruktora

Zasadniczo co chcę zrobić to:

public class A { 
    public A(InputStream stream) { 
     // Do something with the stream but don't close it since we didn't open it 
    } 
} 

public class B { 
    public B(File file) { 
     // We open the stream so we need to ensure it's properly closed 
     try (FileInputStream stream = new FileInputStream(file)) { 
      super(new FileInputStream(file)); 
     } 
    } 
} 

Ale, oczywiście, ponieważ super musi być pierwszą instrukcją w konstruktorze to jest niedozwolone. Czy jest jakiś dobry sposób na osiągnięcie tego?

+2

Chciałbym, aby wywołujący podawał strumień wejściowy do 'public B (InputStream in)' i również to zamknął. Nie ma powodu, aby klasa pochodna była mniej uniwersalna niż klasa podstawowa. – EJP

+0

Bardziej wszechstronny, ale także bardziej nieporęczny w użyciu. Mogłem obsługiwać oba, ale nie mając konstruktora 'B (plik plikowy)' nie jest opcja. – Raniz

+2

Wydaje mi się, że twój problem pochodzi z pochłaniania strumienia wewnątrz konstruktora A. Gdyby tak nie było, po prostu zapisałbyś strumień w zmiennej instancji i wykonałbyś A 'AutoClosable'. –

Odpowiedz

25

Rozważ użycie statycznej metody fabrycznej zamiast bezpośredniego korzystania z konstruktora. Dokonać przynajmniej B „s konstruktora prywatnych oraz stworzenie metody, takie jak

private B(InputStream is) { 
    super(is); 
    // Whatever else is needed 
} 

public static B newInstance(File file) { 
    B result; 
    try (FileInputStream stream = new FileInputStream(file)) { 
     result = new B(stream); 
    } 
    // Further processing 
    return result; 
} 
+0

Tak, wygląda na to, że to jest sposób, aby przejść – Raniz

2

Innym sposobem, aby przejść:

public class A { 
    protected A(){ 
     // so he can't be called from the outside, subclass ensure that init is done properly. 
    } 

    public A(InputStream stream) { 
     init(stream); 
    } 
    // not be able to call it from outside 
    protected final init(InputStream is){ 
     //here goes the code 
    } 
} 

public class B { 
    public B(File file) { 
     // We open the stream so we need to ensure it's properly closed 
     try (FileInputStream stream = new FileInputStream(file)) { 
      init(stream); 
     } 
    } 
} 

jestem delegowania to tutaj jako możliwą odpowiedź, jednak tu jestem consdering:

  1. można aktualizować za kod
  2. ruszasz kod konstruktora do metody init dzięki chronionej konstruktora pusty Arg, tylko podklasy muszą poprawnie obsłużyć wywołanie init. Niektórzy mogą zobaczyć, że nie są tak dobrze zaprojektowane. Chodzi mi o to, że jak tylko zaczniesz coś podklasować, musisz wiedzieć o tym więcej, kiedy właśnie go użyjesz.
+0

Jak już powiedziałeś, wymaga to dostępu do kodu źródłowego * A * i nie zadziała, jeśli istnieją ostateczne zmienne w * A *, które muszą zostać zainicjalizowane z zawartości w strumień. – Raniz

+2

powinieneś zrobić 'init'' final', ponieważ w przeciwnym wypadku podklasa mogłaby go zastąpić i przerwać konstruktor 'A'. Myślę, że to się nazywa wyciek konstruktora lub coś takiego. – Oebele

+0

dobry punkt, dodałem go. – Walfrat

-1

Niestety nie mam pod ręką kompilatora do testowania, ale nie można wykonać następujących czynności.

public class B { 
    private static InputStream file2stream(File f){ 
     // We open the stream so we need to ensure it's properly closed 
     try (FileInputStream stream = new FileInputStream(file)) { 
      return stream; 
     }catch(/*what you need to catch*/){ 
      //cleanup 
      // possibly throw runtime exception 
     } 
    } 
    public B(File file) { 
     super(file2stream(file)) 
    } 
} 
+3

Niestety, koniec bloku 'try' jest osiągany przed końcem' file2stream', zamykając plik, zanim zostanie przekazany do 'super'. –

+0

Być może przegapiłem to pytanie, ale pomyślałem, że o to chodzi. Poddaje mi się takiemu pytaniu, że jedyną rzeczą, która miała być chroniona przez próbę, była konstrukcja InputStream, jeśli tak nie jest, jak należy to zrozumieć? – lijat

+1

Nie rozumiałeś komentarza Hank'a, tak myślę. Problem polega na tym, że w try-for-resources, zasób (strumień), który został otwarty, jest zamykany na końcu bloku.Instrukcja 'return' nie wyklucza tego (lub nie byłoby sensu' try'). Kiedy 'file2stream' zwróci zasób, jest on ** już zamknięty **. Zwróć także uwagę na różnicę między * try-with-resources * (co jest zrobione w celu zamknięcia zasobów) i * try-catch *, która jest wykonywana w celu wychwycenia wyjątków i nie jest tu opisywana. – RealSkeptic

Powiązane problemy