Java >> Java tutorial >  >> Tag >> switch

Mønstertilpasning til switch

1. Oversigt

Java SE 17-udgivelsen introducerer mønstertilpasning til switch udtryk og udsagn (JEP 406) som en forhåndsvisningsfunktion. Mønstermatching giver os mere fleksibilitet, når vi definerer betingelser for switch sager .

Ud over case-etiketter, der nu kan indeholde mønstre, er vælgerudtrykket ikke længere begrænset til kun nogle få typer. Før mønstermatchning, skift cases understøttede kun simpel test af et vælgerudtryk, der skal matche en konstant værdi nøjagtigt.

I denne øvelse vil vi dække tre forskellige mønstertyper, der kan anvendes i switch udsagn. Vi vil også undersøge nogle switch specifikationer, som at dække alle værdier, bestille underklasser og håndtere null-værdier.

2. Skift erklæring

Vi bruger switch i Java for at overføre kontrol til en af ​​de flere foruddefinerede case-sætninger. Hvilket udsagn der bliver valgt afhænger af værdien af ​​switchen vælgerudtryk.

I de tidligere versioner af Java skullevælgerudtrykket være et tal, en streng eller en konstant . Sagsetiketterne kunne også kun indeholde konstanter:

final String b = "B";
switch (args[0]) {
    case "A" -> System.out.println("Parameter is A");
    case b -> System.out.println("Parameter is b");
    default -> System.out.println("Parameter is unknown");
};

I vores eksempel, hvis variabel b ikke var final , ville compileren kaste en konstant udtryk påkrævet fejl.

3. Mønstertilpasning

Mønstermatching blev generelt først introduceret som en preview-funktion i Java SE 14.

Det var begrænset til kun én form for et mønster - typemønsteret. Et typisk mønster består af et typenavn og den variabel, som resultatet skal bindes til.

Anvendelse af typemønstre på forekomsten af ​​ operatør forenkler typekontrol og støbning . Desuden giver det os mulighed for at kombinere begge i et enkelt udtryk:

if (o instanceof String s) {
    System.out.printf("Object is a string %s", s);
} else if (o instanceof Number n) {
    System.out.printf("Object is a number %n", n);
}

Denne indbyggede sprogforbedring hjælper os med at skrive mindre kode med forbedret læsbarhed.

4. Mønstre til Switch

Mønstermatching for instanceof blev en permanent funktion i Java SE 16.

Med Java 17 udvides anvendelsen af ​​mønstertilpasning nu også til at skifte udtryk .

Det er dog stadig en preview-funktion, så vi skal aktivere preview for at bruge den:

java --enable-preview --source 17 PatternMatching.java

4.1. Indtast mønster

Lad os se på, hvordan du skriver mønstre og forekomsten af ​​ operator kan anvendes i switch udsagn.

Som et eksempel vil vi oprette en metode, der konverterer forskellige typer til dobbelt ved hjælp af if-else udsagn. Vores metode returnerer simpelthen nul, hvis typen ikke understøttes:

static double getDoubleUsingIf(Object o) {
    double result;
    if (o instanceof Integer) {
        result = ((Integer) o).doubleValue();
    } else if (o instanceof Float) {
        result = ((Float) o).doubleValue();
    } else if (o instanceof String) {
        result = Double.parseDouble(((String) o));
    } else {
        result = 0d;
    }
    return result;
}

Vi kan løse det samme problem med mindre kode ved at bruge typemønstre i switch :

static double getDoubleUsingSwitch(Object o) {
    return switch (o) {
        case Integer i -> i.doubleValue();
        case Float f -> f.doubleValue();
        case String s -> Double.parseDouble(s);
        default -> 0d;
    };
}

I tidligere versioner af Java var vælgerudtrykket begrænset til kun nogle få typer. Men med typemønstre omskifteren selektorudtryk kan være af enhver type.

4.2. Beskyttet mønster

Typemønstre hjælper os med at overføre kontrol baseret på en bestemt type. Men nogle gange er vi også nødt til at udføre yderligere kontroller af den beståede værdi.

For eksempel kan vi bruge et hvis sætning for at kontrollere længden af ​​en streng :

static double getDoubleValueUsingIf(Object o) {
    return switch (o) {
        case String s -> {
            if (s.length() > 0) {
                yield Double.parseDouble(s);
            } else {
                yield 0d;
            }
        }
        default -> 0d;
    };
}

Vi kan løse det samme problem ved hjælp af beskyttede mønstre. De bruger en kombination af et mønster og et boolesk udtryk:

static double getDoubleValueUsingGuardedPatterns(Object o) {
    return switch (o) {
        case String s && s.length() > 0 -> Double.parseDouble(s);
        default -> 0d;
    };
}

Beskyttede mønstre gør det muligt for os at undgå yderligere hvis betingelser i switch udsagn. I stedet kan vi flyt vores betingede logik til sagsetiketten .

4.3. Mønster i parentes

Ud over at have betinget logik i sagsetiketten giver mønstre i parentes os mulighed for at gruppere dem .

Vi kan simpelthen bruge parenteser i vores booleske udtryk, når vi udfører yderligere kontroller:

static double getDoubleValueUsingParenthesizedPatterns(Object o) {
    return switch (o) {
        case String s && s.length() > 0 && !(s.contains("#") || s.contains("@")) -> Double.parseDouble(s);
        default -> 0d;
    };
}

Ved at bruge parenteser kan vi undgå at have yderligere if-else udsagn.

5. Switch Specifikationer

Lad os nu se på et par specifikke tilfælde, vi skal overveje, når vi bruger mønstermatching i switch .

5.1. Dækker alle værdier

Når du bruger mønstertilpasning i switch , vil Java kompilatoren kontrollere typedækningen .

Lad os overveje et eksempel på switch betingelse, der accepterer ethvert objekt, men kun dækker strengen sag:

static double getDoubleUsingSwitch(Object o) {
    return switch (o) {
        case String s -> Double.parseDouble(s);
    };
}

Vores eksempel vil resultere i følgende kompileringsfejl:

[ERROR] Failed to execute goal ... on project core-java-17: Compilation failure
[ERROR] /D:/Projects/.../HandlingNullValuesUnitTest.java:[10,16] the switch expression does not cover all possible input values

Dette skyldes, at kontakten case-etiketter er påkrævet for at inkludere typen af ​​vælgerudtrykket .

standard Etuietiket kan også anvendes i stedet for en specifik vælgertype.

5.2. Bestilling af underklasser

Når du bruger underklasser med mønstermatchning i switch , rækkefølgen af ​​sagerne har betydning .

Lad os overveje et eksempel, hvor strengen sagen kommer efter CharSequence sag.

static double getDoubleUsingSwitch(Object o) {
    return switch (o) {
        case CharSequence c -> Double.parseDouble(c.toString());
        case String s -> Double.parseDouble(s);
        default -> 0d;
    };
}

Siden String er en underklasse af CharSequence, vores eksempel vil resultere i følgende kompileringsfejl:

[ERROR] Failed to execute goal ... on project core-java-17: Compilation failure
[ERROR] /D:/Projects/.../HandlingNullValuesUnitTest.java:[12,18] this case label is dominated by a preceding case label

Begrundelsen bag denne fejl er, atder er ingen chance for, at udførelsen går til den anden sag da ethvert strengobjekt, der sendes til metoden, ville blive håndteret i selve det første tilfælde.

5.3. Håndtering af nulværdier

I tidligere versioner af Java, hver overførsel af en null værdi til en switch sætning ville resultere i en NullPointerException .

Men med typemønstre er det nu muligt atanvende nul-kontrollen som en separat sagsbetegnelse :

static double getDoubleUsingSwitch(Object o) {
    return switch (o) {
        case String s -> Double.parseDouble(s);
        case null -> 0d;
        default -> 0d;
    };
}

Hvis der ikke er nogen null-specifik case-etiket, vil en mønsteretiket af total type matche null-værdier :

static double getDoubleUsingSwitchTotalType(Object o) {
    return switch (o) {
        case String s -> Double.parseDouble(s);
        case Object ob -> 0d;
    };
}

Vi skal bemærke, at en switch udtryk kan ikke have både en nul sag og en total type sag.

Sådan en switch sætning vil resultere i følgende kompileringsfejl:

[ERROR] Failed to execute goal ... on project core-java-17: Compilation failure
[ERROR] /D:/Projects/.../HandlingNullValuesUnitTest.java:[14,13] switch has both a total pattern and a default label

Til sidst en switch sætning, der bruger mønstermatchning, kan stadig give en NullPointerException .

Det kan dog kun gøre det, når omskifteren blok har ikke en null-matchende case-etiket.

6. Konklusion

I denne artikel undersøgte vi mønstermatching for switch udtryk og udsagn, en preview-funktion i Java SE 17 . Vi så, at ved at bruge mønstre i sagetiketter, bestemmes dette valg af mønstermatching snarere end en simpel lighedskontrol.

I eksemplerne dækkede vi tre forskellige mønstertyper, der kan anvendes i switch udsagn. Til sidst undersøgte vi et par specifikke tilfælde, herunder at dække alle værdier, bestille underklasser og håndtere null-værdier.

Som altid er den komplette kildekode tilgængelig på GitHub.


Java tag