Vra

So, in Java, die eerste lyn van jou constructor HET om'n oproep te super...word dit implisiet die roeping van super(), of uitdruklik die roeping van'n ander kontrakteur.Wat ek wil weet is, hoekom kan ek nie sit'n probeer blok rondom dit?

My spesifieke geval is dat ek het'n spot klas vir'n toets.Daar is geen standaard constructor, maar ek wil een te maak van die toetse makliker om te lees.Ek wil ook om te draai die uitsonderings gegooi van die kontrakteur in'n RuntimeException.

So, wat ek wil doen, is effektief hierdie:

public class MyClassMock extends MyClass {
    public MyClassMock() {
        try {
            super(0);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    // Mocked methods
}

Maar Java kla dat super is nie die eerste stelling.

My tydelike oplossing:

public class MyClassMock extends MyClass {
    public static MyClassMock construct() {
        try {
            return new MyClassMock();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public MyClassMock() throws Exception {
        super(0);
    }

    // Mocked methods
}

Is dit die beste oplossing?Hoekom nie Java, laat my nie die voormalige?


My beste raaiskoot as die "hoekom" is dat Java nie wil hê om my te laat het'n gebou voorwerp in'n potensieel strydig staat...egter, in die doen van'n spot, ek gee nie om nie oor dat.Dit lyk my ek moet in staat wees om te doen die bogenoemde...of ten minste ek weet dat die bogenoemde is veilig vir my geval...of lyk asof dit behoort te wees in elk geval.

Ek is oorheersende enige metodes wat ek gebruik van die getoets klas, so daar is geen risiko dat ek met behulp van uninitialized veranderlikes.

Was dit nuttig?

Oplossing

Ongelukkig opstellers kan nie werk nie op teoretiese beginsels, en selfs al is jy kan weet dat dit veilig is in jou geval, indien hulle toegelaat word om dit, sou dit veilig is vir alle gevalle te wees.

Met ander woorde, die samesteller is nie net jy stop, dit stop almal, insluitende almal wat nie weet wat dit is onveilig en moet spesiale hantering. Daar is waarskynlik ander redes hiervoor sowel as alle tale gewoonlik maniere om onveilig dinge doen as 'n mens weet hoe om dit te hanteer.

In C # NET daar soortgelyke bepalings, en die enigste manier om 'n konstruktor wat 'n basis constructor noem verklaar is:

public ClassName(...) : base(...)

om dit te doen, die basis constructor sal genoem word voordat die liggaam van die konstruktor, en jy kan hierdie volgorde nie verander nie.

Ander wenke

Dit is gedoen om te verhoed dat iemand uit die skep van 'n nuwe SecurityManager voorwerp van vertrou kode.

public class Evil : SecurityManager {
  Evil()
  {
      try {
         super();
      } catch { Throwable t }
      {
      }
   }
}

Ek weet dit is'n ou vraag, maar ek hou van dit, en as sodanig, ek het besluit om te gee dit'n antwoord van my eie.Miskien is my begrip van waarom dit kan nie gedoen word sal bydra om die bespreking en die toekomstige lesers van jou interessante vraag.

Laat my begin met'n voorbeeld van die versuim voorwerp konstruksie.

Kom ons definieer'n klas A, sodanig dat:

class A {
   private String a = "A";

   public A() throws Exception {
        throw new Exception();
   }
}

Nou, kom ons veronderstel ons wil graag om te skep van'n voorwerp van'n tipe in'n try...catch blok.

A a = null;
try{
  a = new A();
}catch(Exception e) {
  //...
}
System.out.println(a);

Klaarblyklik, die uitset van hierdie kode sal wees: null.

Hoekom Java kom nie terug nie'n gedeeltelik gebou weergawe van A?Na alles, deur die punt wat die kontrakteur versuim, die voorwerp se name die veld is reeds geïnisialiseer, reg?

Wel, Java kan nie terugkeer'n gedeeltelik gebou weergawe van A omdat die voorwerp was nie suksesvol gebou is.Die voorwerp is in'n strydig staat, en dit is dus weggegooi deur Java.Jou veranderlike is nie, selfs nie geïnisialiseer, dit is gehou as van nul.

Nou, soos jy weet, om ten volle te bou van'n nuwe voorwerp, al sy super klasse moet wees geïnisialiseer eerste.As een van die super klasse versuim het om uit te voer, wat sou die finale toestand van die voorwerp?Dit is onmoontlik om te bepaal.

Kyk na hierdie meer uitgebreide voorbeeld

class A {
   private final int a;
   public A() throws Exception { 
      a = 10;
   }
}

class B extends A {
   private final int b;
   public B() throws Exception {
       methodThatThrowsException(); 
       b = 20;
   }
}

class C extends B {
   public C() throws Exception { super(); }
}

Wanneer die kontrakteur van C is geroep, as'n uitsondering plaasvind terwyl initializing B, wat sou die waarde van die finale int veranderlike b?

As sodanig, die voorwerp C kan nie geskep word, dit is vals, is dit die asblik, dit is nie ten volle geïnisialiseer.

Vir my, dit verduidelik waarom jou kode is onwettig.

Ek kan nie veronderstel om te het'n diep begrip van Java internals, maar dit is my begrip dat, wanneer'n samesteller behoeftes te instansieer'n afgeleide klas, het dit eers die basis (en sy basis voor wat(...)) en dan klap op die uitbreidings gemaak in die subklas.

So dit is nie eens die gevaar van uninited veranderlikes of iets soos dit by al.Wanneer jy probeer om iets te doen in die subklas' constructor voor die basis klas" constructor, jy is basies vra die samesteller uit te brei'n basis voorwerp instansie wat nog nie bestaan nie.

Edit:In jou geval, MyClass word die basis voorwerp, en MyClassMock is'n subklas.

Ek weet nie hoe Java intern geïmplementeer, maar as die konstruktor van die superklas gooi 'n uitsondering, dan is daar nie 'n geval van die klas wat jy brei. Dit sou onmoontlik wees om die toString() of equals() metodes noem, byvoorbeeld, aangesien hulle geërf het in die meeste gevalle.

Java mag 'n drie / vangs rondom die super () oproep in die konstruktor as 1. jy al die metodes van die superclasses ignoreer toelaat, en 2. jy nie die super.XXX () klousule gebruik, maar dat almal klanke te ingewikkeld vir my.

Ek weet hierdie vraag het talle antwoorde, maar ek wil graag my klein versnapering te gee oor hoekom dit nie toegelaat sou word, spesifiek om te antwoord waarom Java nie toelaat om dit te doen. So hier gaan ons ...

Nou, in gedagte hou dat super() het genoem te word voordat enige iets anders in constructor 'n subklas se, so, as jy het gebruik try en catch blokke rondom jou super() oproep, die blokke sal hê om te lyk soos hierdie:

try {
   super();
   ...
} catch (Exception e) {
   super(); //This line will throw the same error...
   ...
}

As super () fails in thetryblock, it HAS to be executed first in thecatchblock, so thatsuperruns before anything in your subclasss constructor. Dit laat jou met dieselfde probleem wat jy het aan die begin: as 'n uitsondering is gegooi, is dit nie gevang. (In hierdie geval is dit net kry weer gegooi in die vangs blok.)

Nou, die bo-kode is op geen manier deur Java toegelaat nie. Hierdie kode kan die helfte van die eerste super oproep uit te voer, en dan noem dit weer, wat 'n paar probleme met 'n paar super klasse kan veroorsaak.

Nou, die rede dat Java nie toelaat dat jy 'n uitsondering te gooi in plaas van 'n beroep super() is omdat die uitsondering iewers anders kon gevang word, en die program sal voortgaan sonder om super() op jou subklas voorwerp, en moontlik omdat die uitsondering jou voorwerp kon neem as 'n parameter en probeer om die waarde van geërf het byvoorbeeld veranderlikes, wat sou nog nie geïnisialiseer verander.

Een manier om dit te kry is deur te bel 'n private statiese funksie. Die drie-vangs kan dan in die funksie liggaam geplaas word.

public class Test  {
  public Test()  {
     this(Test.getObjectThatMightThrowException());
  }
  public Test(Object o)  {
     //...
  }
  private static final Object getObjectThatMightThrowException()  {
     try  {
        return  new ObjectThatMightThrowAnException();
     }  catch(RuntimeException rtx)  {
        throw  new RuntimeException("It threw an exception!!!", rtx);
     }
  }
}
Gelisensieer onder: CC-BY-SA met toeskrywing
Nie verbonde aan StackOverflow
scroll top