Kontrollerer, om en klasse findes i Java

1. Oversigt

At kontrollere for eksistensen af ​​en klasse kan være nyttigt, når man bestemmer hvilken implementering af en grænseflade, der skal bruges. Denne teknik bruges ofte under ældre JDBC-opsætninger.

I denne vejledning vi udforsker nuancerne ved at bruge Class.forName () for at kontrollere eksistensen af ​​en klasse i Java-klassestien.

2. Brug Class.forName ()

Vi kan tjekke for eksistensen af ​​en klasse ved hjælp af Java Reflection, specifikt Class.forName (). Dokumentationen viser, at en ClassNotFoundException kastes, hvis klassen ikke kan findes.

2.1. Hvornår kan man forvente ClassNotFoundException

Lad os først skrive en test, der helt sikkert vil kaste en ClassNotFoundException så vi kan vide, at vores positive tests er sikre:

@Test (forventet = ClassNotFoundException.class) offentlig ugyldighed givenNonExistingClass_whenUsingForName_thenClassNotFound () kaster ClassNotFoundException {Class.forName ("class.that.does.not.exist"); }

Så vi har bevist, at en klasse, der ikke eksisterer, vil kaste et ClassNotFoundException. Lad os skrive en test til en klasse, der virkelig findes:

@Test offentlig ugyldighed givenExistingClass_whenUsingForName_thenNoException () kaster ClassNotFoundException {Class.forName ("java.lang.String"); }

Disse tests beviser det kører Class.forName () og ikke fange en ClassNotFoundException svarer til den angivne klasse, der findes på klassestien. Dette er dog ikke helt en perfekt løsning på grund af bivirkninger.

2.2. Bivirkning: Initialisering af klasse

Det er vigtigt at påpege, at uden at specificere en klasselæsser, Class.forName () skal køre den statiske initialisering på den anmodede klasse. Dette kan føre til uventet opførsel.

Lad os eksemplificere denne adfærd ved at oprette en klasse, der kaster en RuntimeException når dens statiske initialiseringsblok udføres, så vi kan vide straks, hvornår den udføres:

offentlig statisk klasse InitializingClass {statisk {hvis (sand) {// muliggør kastning af en undtagelse i en statisk initialiseringsblok kast ny RuntimeException (); }}}

Vi kan se fra forName () dokumentation for, at det kaster en ExceptionInInitializerError hvis initialiseringen fremkaldt af denne metode mislykkes.

Lad os skrive en test, der forventer en ExceptionInInitializerError når vi prøver at finde vores Initialisering af klasse uden at specificere en klasselæsser:

@Test (forventet = ExceptionInInitializerError.class) offentlig ugyldighed givenInitializingClass_whenUsingForName_thenInitializationError () kaster ClassNotFoundException {Class.forName ("path.to.InitializingClass"); }

Da udførelsen af ​​en klasses statisk initialiseringsblok er en usynlig bivirkning, kan vi nu se, hvordan det kan forårsage ydelsesproblemer eller endda fejl. Lad os se på, hvordan du springer over initialisering af klassen.

3. Fortælling Class.forName () for at springe initialisering over

Heldigvis for os er der en overbelastet metode til forName (), der accepterer en klasselæsser, og om klassens initialisering skal udføres.

Ifølge dokumentationen er følgende opkald ækvivalente:

Class.forName ("Foo") Class.forName ("Foo", sandt, this.getClass (). GetClassLoader ())

Ved at ændre rigtigt til falsk, kan vi nu skrive en test, der kontrollerer eksistensen af ​​vores Initialisering af klasseuden at udløse dens statiske initialiseringsblok:

@Test offentlig ugyldighed givenInitializingClass_whenUsingForNameWithoutInitialization_thenNoException () kaster ClassNotFoundException {Class.forName ("path.to.InitializingClass", false, getClass (). GetClassLoader ()); }

4. Java 9-moduler

For Java 9+ projekter er der en tredje overbelastning af Class.forName (), som accepterer en Modul og en Snor klasse navn. Denne overbelastning kører ikke klasseinitialiseringen som standard. Især vender det tilbage nul når den anmodede klasse ikke findes snarere end at kaste en ClassNotFoundException.

5. Konklusion

I denne korte vejledning har vi eksponeret bivirkningen ved klasseinitialisering, når du bruger Class.forName () og har fundet ud af, at du kan bruge forName () overbelastning for at forhindre, at det sker.

Kildekoden med alle eksemplerne i denne vejledning kan findes på GitHub.