Java 8 voor Android Cleaner Code met Lambda Expressions

Lambda-expressies kunnen u helpen bij het verwijderen van boilerplate-code uit uw projecten en het verwerken van enorme hoeveelheden gegevens met gemak. Zie hoe u met deze diepgaande blik op de Java 8-functies die u vandaag in uw Android-projecten kunt gebruiken. 

Java 8 voor Android

Java 8, dat in maart 2014 debuteerde, was een grote stap voorwaarts voor de programmeertaal en introduceerde een lijst met functies die beloofden codering in Java eenvoudiger en beknopter te maken dan ooit tevoren.

Helaas, Android-ontwikkelaars zouden de voordelen van deze functies al een tijdje niet voelen, omdat Google experimenteerde met het brengen van Java 8 naar het Android-platform via Jack (Java Android Compilerkit) voordat Jack werd afgekeurd ten gunste van het native ondersteunen van Java 8 in Android Studio.

Nu, met de release van Android Studio 3.0, hebben we eindelijk een versie van de Android-toolchain met ingebouwde ondersteuning voor enkele van de belangrijkste functies van Java 8.

In deze serie laat ik je zien hoe je een hoop boilerplate-codes uit je projecten kunt verwijderen, enorme hoeveelheden gegevens gemakkelijk kunt verwerken en zelfs een meer functionele stijl kunt omarmen in je Java-programmering met Java 8. We zullen een diepgaande blik werpen op de Java 8-functies die u vandaag kunt gebruiken.

Tegen de tijd dat u deze serie hebt voltooid, bent u klaar om alle volgende Java 8-functies in uw Android-projecten te gebruiken:

  • lambda-expressies
  • methode referenties
  • standaard methoden
  • statische interfacemethoden
  • type annotaties
  • annotaties herhalen
  • functionele interfaces
  • de Stream-API

In deze eerste post gaan we kijken naar de functie die de meeste buzz veroorzaakte toen Java 8 voor het eerst werd uitgebracht, en die het potentieel heeft om het grootste verschil te maken voor Android-ontwikkelaars: lambda-expressies.

Uw ontwikkelomgeving voorbereiden

Voordat u kunt beginnen met gebruiken ieder Java 8-functies, moet u ervoor zorgen dat uw ontwikkelomgeving is ingesteld om deze versie van Java te ondersteunen.

Als u Java 8 nog niet hebt geïnstalleerd, moet u de nieuwste JDK8 downloaden en het JDK-pad van Android Studio bijwerken, zodat het verwijst naar het JDK8-pakket:

  • Start Android Studio.
  • kiezen Bestand> Projectstructuur ... van de Android Studio-werkbalk.
  • Werk het JDK-locatie zodat het verwijst naar uw nieuw gedownloade JDK8-pakket.

Als u niet zeker weet welke versie van Java u hebt geïnstalleerd, kunt u controleren door een Terminal-venster te openen (als u een Mac-gebruiker bent) of een opdrachtprompt (als u Windows gebruikt) en vervolgens het volgende uitvoeren commando:

java -version

Als het build 1,8 of hoger oplevert, dan ben je klaar om te gaan!

Je moet ook Android Studio 3.0 Preview 1 of hoger geïnstalleerd hebben, maar om je kans op bugs en ander vreemd gedrag te verkleinen, is het aan te raden de nieuwste versie van Android Studio 3.0 te installeren, of dat nu een bètaversie of een voorbeeld is, of, idealiter een stabiele versie van Android Studio 3.0 (die op het moment van schrijven nog niet beschikbaar was). 

Vervolgens moet u enkele wijzigingen in uw project aanbrengen build.gradle bestanden. Meestal hoeft u slechts enkele regels code toe te voegen die aangeven dat dit project Java 8 bytecode moet genereren. Als je echter eerder hebt geëxperimenteerd met Java 8-functies met de Jack-compiler of het populaire Retrolambda-project, moet je deze tools uitschakelen voordat je project de nieuwe en verbeterde Java 8-ondersteuning kan gebruiken die wordt geboden door de standaard toolchain van Android..

In de volgende secties laat ik zien hoe u ondersteuning voor Java 8 inschakelt en hoe u Retrolambda en Jack desgewenst uitschakelt.

Java 8-ondersteuning toevoegen aan een nieuw project

Ervan uitgaande dat u Jack nog niet eerder hebt ingeschakeld of Retrolambda hebt toegevoegd als projectafhankelijkheid, is de eerste stap het openen van uw projectniveau build.gradle bestand en zorg ervoor dat u versie 3.0.0-alpha1 (of hoger) van de Gradle voor Android-plug-in gebruikt:

buildscript repositories google () jcenter () afhankelijkheden classpath 'com.android.tools.build:gradle:3.0.0-alpha6'

Open vervolgens elk moduleniveau build.gradle bestand waar u Java 8-functies wilt gebruiken en stel het taalniveau van de broncode in en de versie van de gegenereerde Java bytecode naar JavaVersion.VERSION_1_8:

android compileSdkVersion 26 buildToolsVersion "26.0.1" defaultConfig applicationId "com.jessicathornsby.myapplication" minSdkVersion 26 targetSdkVersion 26 versionCode 1 versionName "1.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" // Voeg het volgende blok toe // compileOptions sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8

Als u van Jack migreert

De Jack-compiler is mogelijk verouderd, maar zolang deze is ingeschakeld, gaat je project de Java 8-ondersteuning gebruiken die door Jack wordt geboden, in plaats van de ondersteuning door de standaard toolchain van Android..

Het gebruik van een verouderde tool is nooit een goed idee, maar er zijn enkele extra redenen waarom je zou moeten migreren van de Jack-compiler, als je dat nog niet hebt gedaan.

Ten eerste kan Jack een deel van de Java 8-functies ondersteunen, maar in tegenstelling tot de standaard toolchain ondersteunt het geen externe bibliotheken die deze functies gebruiken, dus door Jack te gebruiken, beperk je onmiddellijk je opties als het gaat om bibliotheken van derden.

Ten tweede neemt de Jack-compiler Java-code en converteert deze direct naar dex, zonder tussenliggende bytecodes te produceren. Zolang Jack is ingeschakeld, kunt u geen van de hulpprogramma's gebruiken die afhankelijk zijn van deze tussenproductuitvoer, zoals annotatieprocessors en bytecodeanalysators..

Om de Jack-compilator uit te schakelen, opent u uw moduleniveau build.gradle bestand en verwijder de jackOptions sectie, maar zorg ervoor dat u de compileOptions blokkeer intact:

android compileSdkVersion 26 buildToolsVersion "26.0.1" defaultConfig applicationId "com.jessicathornsby.myapplication" minSdkVersion 26 targetSdkVersion 26 versionCode 1 versionName "1.0" // Verwijder de gehele sectie jackOptions // jackOptions enabled true testInstrumentationRunner "android.support. test.runner.AndroidJUnitRunner "// Het gedeelte compileOptions niet verwijderen // compileOptions sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8

Als u vanuit Retrolambda migreert

Net als bij Jack ondersteunt Retrolambda geen externe bibliotheken die Java 8-taalfuncties gebruiken. Als uw project is ingesteld om de Retrolambda-plug-in te gebruiken, moet u deze plug-in verwijderen, zodat uw project kan terugkeren naar de standaard toolchain.

Open uw projectniveau build.gradle bestand en verwijder Retrolambda als een projectafhankelijkheid:

 afhankelijkheden classpath 'com.android.tools.build:gradle:3.0.0-beta2' // Verwijder de volgende regel // classpath 'me.tatarka: gradle-retrolambda: 3.7.0'

Verwijder vervolgens de Retrolambda-plug-in van elk van uw moduleniveau build.gradle bestanden:  

plug-in toepassen: 'com.android.application' // Verwijder de volgende regel // plug-in toepassen: 'me.tatarka.retrolambda' android ... // Het blok compileOptions niet verwijderen! // compileOptions sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8

Test uw Java 8-ondersteuning

De eenvoudigste manier om te controleren of uw project nu Java 8 kan ondersteunen, is door een snelle lambda-expressie te schrijven en te kijken of uw project nog steeds compileert.

Voeg een knop toe aan uw gebruikersinterface (of gebruik een knop die al bestaat) en implementeer vervolgens een onClickListener voor deze knop, met behulp van een lambda-expressie. Maakt u zich geen zorgen als de volgende code nu niet zo duidelijk is, zoals aan het einde van dit artikel!

import android.support.v7.app.AppCompatActivity; import android.os.Bundle; importeer android.widget.Button; import android.view.View; import android.widget.Toast; public class MainActivity breidt AppCompatActivity uit @Override protected void onCreate (Bundle savedInstanceState) super.onCreate (savedInstanceState); setContentView (R.layout.activity_main); // Implementeer de onClickListener met een lambda-uitdrukking // Knop button = (Button) findViewById (R.id.button); if (button! = null) button.setOnClickListener ((View view) -> Toast.makeText (this, "I was written in Java 8!", Toast.LENGTH_LONG) .show ()); 

Controleer of je project nog steeds compileert, hetzij door te selecteren Synchroniseren van de banner die verschijnt, of door te selecteren Hulpmiddelen> Android> Sync-project met groepsbestanden van de Android Studio-werkbalk.

Als Android Studio geen fouten veroorzaakt, bent u klaar om alle functies te gebruiken die we aan het begin van dit artikel hebben genoemd, inclusief lambda-expressies!

Waarom zijn Lambda-uitingen zo belangrijk?

Lambda-expressies waren gemakkelijk de belangrijkste nieuwe functie van Java 8 en kunnen een reusachtig invloed op de hoeveelheid boilerplate code die je moet schrijven bij het maken van vrijwel elke Android-app.

In wezen vertegenwoordigt een lambda-uitdrukking een functie die niet tot een klasse behoort, en die je gemakkelijk kunt doorgeven en vervolgens op verzoek kunt uitvoeren.

Deze functie verwijdert een langdurige frustratie die veel Android-ontwikkelaars hebben ervaren met Java: als een object-georiënteerde taal, het doorgeven van blokken code heeft altijd voelde moeilijker dan het zou moeten zijn. Als u bijvoorbeeld een nieuwe thread wilt maken en vervolgens wat code aan die thread wilt doorgeven, moet u meestal een thread maken met een anonieme implementatie van de Runnable-interface, dat is veel werk, alleen maar om een ​​code door te geven! Door lambda-expressies een eenvoudige manier te bieden om een ​​functie aan een methode door te geven, kunnen sommige van de meest voorkomende taken die u als Android-ontwikkelaar zult uitvoeren, worden vereenvoudigd.

Lambda-expressies zullen ook een welkome toevoeging zijn voor alle Java-ontwikkelaars die een meer functionele benadering van hun programmering willen. Voor Java 8 zou coderen in een functionele stijl onvermijdelijk vereisen dat je veel boilerplate code schrijft, maar nu je functies kunt gebruiken rond lambda-expressies, is het niet nodig om je Java-code op een minder objectgerichte manier te schrijven een hoop anonieme lessen schrijven.

Hoe maak ik een Lambda-expressie?

U maakt een lambda-expressie met behulp van de volgende syntaxis:

(argument) -> expressie body

De beheerder van de pijl spreekt redelijk voor zich uit, maar de regels voor het structureren van het argument en de uitdrukking van de lambda kunnen variëren, afhankelijk van wat je probeert te bereiken. Laten we deze twee elementen eens nader bekijken..

Het argument

Het argument is een of meer parameters, dat zijn bijna altijd ingesloten tussen haakjes. Zelfs als je lambda-uitdrukking geen parameters heeft, moet je nog steeds lege haakjes opgeven, bijvoorbeeld:

() -> System.out.println ("Deze lambda-uitdrukking heeft geen parameters");

De uitzondering op deze regel is wanneer uw methode één parameter heeft met het type afgeleid, in welk geval u de haakjes kunt weglaten:

textView.setOnLongClickListener (event -> System.out.println ("Lange klik"));

U kunt meerdere parameters in uw argument gebruiken door elke parameter te scheiden met een komma:

(parameter1, parameter2) -> System.out.println ("Parameters:" + parameter1 + "," + parameter2);

Type gevolgtrekking is mogelijk in lambda's, dus u kunt over het algemeen het gegevenstype weglaten uit uw argument. Als de compiler het gegevenstype echter niet kan afleiden, moet u het type vóór uw parameter (s) toevoegen:

 Knop knop = (Knop) findViewById (R.id.button); if (button! = null) button.setOnClickListener ((View view) -> Log.d ("debug", "Button aangeklikt")); 

Het uitdrukkingsorgaan

De expressie-instantie is de code die u wilt uitvoeren, wat een enkele expressie of meerdere regels code kan zijn. Als u meerdere regels wilt uitvoeren, moet u een instructieblok maken door dit gedeelte van uw code te omringen met accolades:

Knop knop = (Knop) findViewById (R.id.button); button.setOnClickListener (weergave -> Log.d ("debug", "Button clicked"); Toast.makeText (this, "I was written in Java 8!", Toast.LENGTH_LONG) .show ();

Als uw expressie een waarde retourneert, moet deze worden geretourneerd met een retourinstructie, bijvoorbeeld:

(parameter1) -> System.out.println ("Parameter:" + parameter1); return "return value"; 

Lambda Expressions gebruiken in uw Android-apps

Nu hebben we een overzicht van de verschillende manieren waarop je een lambda-expressie kunt structureren, laten we een paar van de meest voorkomende scenario's bekijken waarin je lambda-expressies kunt gebruiken in je Android-ontwikkelingswerk.

Lambdas voor evenementen

Uw typische Android-app moet kunnen reageren op een breed scala aan gebruikersinvoergebeurtenissen, en lambda-expressies kunnen deze gebeurtenisafhandeling veel eenvoudiger maken.

In de volgende code gebruiken we een anonieme klasse om een ​​instantie van te maken onClickListener met een overschreven bij klikken methode. De kans is groot dat je dit soort code hebt geschreven ontelbaar tijden.

Knop knop = (Knop) findViewById (R.id.button); button.setOnClickListener (nieuwe View.OnClickListener () @Override public void onClick (View view) doSomething (););

Door de bovenstaande code te herschrijven met een lambda-uitdrukking, kunnen we al het volgende verwijderen:

  • de klasse-instantiatie: nieuwe View.OnClickListener ()
  • de toegangsmodificator, methode naam en type: openbare ongeldig onClick (bekijk weergave)
  • en de parametertypen, zodat u niet hoeft te schrijven Bekijk weergave

Dit betekent dat we precies dezelfde functionaliteit kunnen implementeren, met behulp van een enkele regel:

button.setOnClickListener (weergave -> doSomething ());

Lambdas voor Multithreading

Multithreading is een ander veelvoorkomend scenario waarin lambda-expressies u kunnen helpen schonere code te schrijven. Standaard heeft Android één UI-interface (gebruikersinterface) die verantwoordelijk is voor alle gebruikersinteracties, gebeurtenissen verzendt naar de juiste UI-widgets en de gebruikersinterface wijzigt. Zodra u deze UI-thread blokkeert bij langdurige of intensieve bewerkingen, reageert uw toepassing niet meer en wordt mogelijk Android ANR (Application Not Responding) -dialoog geactiveerd. Dus het maken van extra threads en het toewijzen van code voor het uitvoeren van die threads is vaak een essentieel onderdeel van Android-ontwikkeling.

Vóór Java 8 moest u een code toewijzen om op een extra thread te worden uitgevoerd om een ​​anonieme klasse te maken die de. Implementeert uitvoerbare interface:

Runnable r = new Runnable () @Override public void run () System.out.println ("My runnable"); ; Thread thread = new Thread (r); thread.start ();

U kunt ook een nieuwe thread maken met een anonieme implementatie van de uitvoerbare interface:

Thread thread = new Thread (nieuw Runnable () @Override public void run () System.out.println ("My runnable");); thread.start ();

Het vervangen van deze anonieme klasse door een lambda-uitdrukking kan deze vaak uitgevoerde taak maken veel beknopter:

Runnable r = () -> System.out.println ("My runnable"); ; // Start de nieuwe thread // new Thread (r) .start ();

Als u tenslotte de bibliotheek RxJava of RxAndroid gebruikt, kunt u lambda-expressies gebruiken om observables te maken.

Hier creëren we een simpele waarneembaar dat de hallo wereldreeks uitzendtvoor al zijn waarnemers:

Observable.just ("Hello, world!") .Subscribe (nieuwe actie1() @Override public void call (String s) Log.d (TAG, s); );

Als je een lambda-expressie gebruikt, kun je dat allemaal vervangen Action1 code met een enkele regel:

Observable.just ("Hello, world!") .Subscribe (s -> Log.d (TAG, s));

Lambda-expressies gebruiken in uw Echte leven Code

Na het lezen van alle theorieën achter een nieuwe functie, is de volgende uitdaging om gewoon te worden gebruik makend van deze nieuwe functie. Dit kan bijzonder moeilijk zijn met zoiets als lambda's, die zijn ontworpen om te worden gebruikt in plaats van vertrouwde boilerplate-code, omdat er altijd de verleiding is om gewoon terug te vallen op wat je weet.

Android Studio heeft een aantal functies die u kunnen helpen bij het laatste duwtje om vertrouwde-maar-onhandige code te vervangen door glanzende nieuwe lambda-expressies.

De eerste functie is het intentacties-menu van Android Studio, waarmee elke compatibele anonieme klasse automatisch kan worden geconverteerd naar de equivalente lambda-expressie. Dit is perfect als je je ooit afvraagt ​​hoe je een bepaald stuk code in een lambda-formaat kunt schrijven: schrijf het gewoon zoals gebruikelijk en gebruik dan de functie voor automatisch converteren van het intent-actiemenu.

Om een ​​anonieme klasse automatisch om te zetten in een lambda-uitdrukking:

  • Beweeg uw cursor over de anonieme klasse en Android Studio moet een tooltip weergeven waarin u wordt geïnformeerd dat dit gedeelte van de code kan worden geconverteerd naar een lambda-uitdrukking.
  • Druk op je Mac's Alt / Option-Enter toetsen of gebruik de Alt-Enter sneltoets als u een Windows- of Linux-gebruiker bent.
  • Selecteer de Vervangen door lambda optie uit het contextmenu.


U kunt ook het Inspection-hulpmiddel van Android Studio gebruiken om te markeren elk anonieme klasse die u mogelijk zou kunnen vervangen door een lambda-expressie, voor uw hele project. Vervolgens kunt u elke anonieme klasse handmatig herschrijven of de automatische conversiefunctie van Android Studio laten zien hoe het werkt.

Om elke anonieme klasse te markeren die Android Studio mogelijk zou kunnen vervangen door een lambda-expressie:

  • kiezen Analyseren> Inspectie uitvoeren op naam van de Android Studio-werkbalk.
  • In de pop-up die verschijnt, begin met typen Het anonieme type kan worden vervangen door lambda, en selecteer vervolgens deze optie wanneer deze wordt weergegeven in het vervolgkeuzemenu.


  • Selecteer in het volgende venster Hele project om elke anonieme klasse in uw project te markeren. U kunt ook afzonderlijke modules of bestanden opgeven waarop Android Studio deze inspectie moet uitvoeren.
  • Klik OK.
  • kiezen Analyseren> Code inspecteren van de Android Studio-werkbalk.

Het deelvenster Inspectieresultaten zou nu moeten verschijnen en een lijst weergeven van alle anonieme klassen die u kunt vervangen door een lambda-uitdrukking. Om een ​​anonieme klasse van dichterbij te bekijken, gewoon Dubbelklik die klasse in de Inspectie resultaten venster en Android Studio opent het bestand en neemt u mee naar de exacte regel die deze specifieke anonieme klasse bevat.

Om de momenteel geselecteerde anonieme klasse te vervangen door een lambda-uitdrukking, geeft u de Vervangen door lambda druk op een klik.


Als Android Studio het venster Inspectieresultaten niet automatisch opent, kunt u het handmatig starten door te selecteren Beeld> Tool Windows> Inspectie resultaten van de Android Studio-werkbalk. Als inspectieresultaten niet verschijnen in de Gereedschap Windows submenu, dan moet u mogelijk selecteren Analyseren> Code inspecteren ... van de Android Studio-werkbalk eerst.

Lambda-expressies testen

Ondanks de vele voordelen die lambda-expressies te bieden hebben, is er een belangrijk nadeel dat u moet weten voordat u ze aan uw code toevoegt. Omdat lambda's geen naam hebben, kun je ze niet rechtstreeks vanuit je testcode bellen, dus het toevoegen van een groot aantal lambda's aan je project kan het moeilijker maken om te testen.

Idealiter zouden je lambda-uitdrukkingen te simpel moeten zijn om te breken, dus het niet in staat zijn om ze te testen, zou niet al te groot moeten zijn. Als je echter een lambda moet testen, dan kun je het altijd als een privémethode behandelen en de unit de. Testen resultaat, in plaats van de lambda zelf. Als alternatief zou je de lambda-expressie in zijn eigen methode kunnen refactoren, zodat je er direct naar kunt verwijzen en het dus als normaal kunt testen.  

Conclusie

In dit eerste bericht over de Java 8-taalfuncties hebben we gekeken naar hoe u uw Android-projecten kunt instellen om Java 8 te ondersteunen en hoe u de boilerplate-code kunt verlagen door anonieme klassen te vervangen door lambda-expressies. 

In het volgende bericht laat ik je zien hoe je nog meer code uit je Android-projecten kunt trimmen door lambda-expressies te combineren met methodeverwijzingen en hoe je je interfaces kunt verbeteren met standaard en statische interfacemethoden.

Bekijk in de tussentijd een aantal van onze andere berichten over de ontwikkeling van Android-apps!