redundant cast til java.lang.Object advarsel for nødvendig cast
Fejlen om en "ambiguous method invocation" er korrekt siden Java 8.
Allerede før Java 8 kunne du skrive
char c = fail();
Object o = fail();
uden compiler fejl. Når du passerer en betinget som condition? 'a': genericMethod()
til en metode som String.valueOf(…)
, udledte compileren <Object>
for fail()
og valgte String.valueOf(Object)
på grund af dens begrænsede typeslutning.
Men Java 8 introducerede Poly Expressions:
Typen af et selvstændigt udtryk kan bestemmes helt ud fra indholdet af udtrykket; i modsætning hertil kan typen af et polyudtryk være påvirket af udtrykkets måltype (§5 (Konverteringer og kontekster)).
Både en påkaldelse af en generisk metode og en betinget, der indeholder et polyudtryk (dvs. påkaldelsen af en generisk metode), er polyudtryk.
Så prøver at påkalde String.valueOf(char)
er gyldig med det betingede, som vi kan udlede <Character>
for fail()
. Bemærk, at ingen af metoderne er anvendelige i en streng invokationskontekst, da begge varianter kræver en boksning eller unboxing-operation. I en løs invokationskontekst, begge, String.valueOf(Object)
og String.valueOf(char)
er gældende, da det er ligegyldigt, om vi udpakker Character
efter at have påkaldt fail()
eller boks char
af den bogstavelige 'a'
.
Siden char
er ikke en undertype af Object
og Object
er ikke en undertype af char
, ingen af metoderne, String.valueOf(Object)
heller ikke String.valueOf(char)
, er mere specifik, og derfor genereres en kompileringsfejl.
Det er sværere at bedømme advarslen, da der ikke er nogen formel standard for advarsler. Efter min mening er enhver compiler-advarsel, der hævder, at en kildekodeartefakt var forældet på trods af koden, ikke vil gøre det samme efter at have fjernet den (eller at fjerne den vil endda introducere fejl), er forkert. Interessant nok findes advarslen allerede i Java 7's version af javac
, hvor det ikke gør nogen forskel at fjerne rollebesætningen, så måske er det en rest, der skal opdateres.
Løsninger til problemet afhænger af konteksten, og der er ikke nok information om det. Vær opmærksom på, at der kun er behov for én gren, som ikke kan tildeles char
, for at gøre metoden String.valueOf(char)
uanvendelig. Det vil ske, så snart du indsætter grenen, der evaluerer til String
. Du kan også bruge SurroundingClass.<Object>fail()
for at komme til den samme type, som kompilatorer før Java 8 udledte.
Eller slip helt den generiske signatur, da den ikke er nødvendig her. Den generiske metode fail()
synes at være en work-around at have en kastemetode i en udtrykssammenhæng. En renere løsning ville være en fabriksmetode til udtrykket, f.eks.
class Code {
public static void main(String[] args) throws SpecificExceptionType {
System.out.println(
String.valueOf(switch(0) {
case 0 -> 'a';
case 1 -> 'b';
case 2 -> 'c';
default -> throw fail();
})
);
}
private static SpecificExceptionType fail() {
return new SpecificExceptionType();
}
static class SpecificExceptionType extends Exception {
}
}
Hvis skifteudtryk ikke er mulige, kan du bruge
System.out.println(
String.valueOf(
true ? 'a' :
true ? 'b' :
true ? 'c' :
Optional.empty().orElseThrow(Code::fail)
)
);
Begge har fordelen af at være specifikke med hensyn til den faktiske type af potentielt kastede undtagelser og behøver ikke at ty til ukontrollerede undtagelser eller throws Throwable
erklæringer. Den anden kan føles hacky, men ikke mere end at definere en generisk metode, der aldrig returnerer noget.
Selvfølgelig er der andre muligheder for at løse det, hvis du bare accepterer introduktionen af mere kode, som en dedikeret hjælpemetode til strengkonvertering uden overbelastning eller en ikke-generisk indpakningsmetode til kastemetoden. Eller en midlertidig variabel eller type casts eller eksplicitte typer for de generiske påkald osv. Yderligere, når du bruger "" + (expression)
eller (expression).toString()
i stedet for String.valueOf(expression)
, udtrykket er ikke et polyudtryk, og det producerer derfor ikke en "ambiguous method invocation"-fejl.
Da dette er en falsk advarsel, kan du selvfølgelig også beholde rollebesætningen og tilføje en @SuppressWarnings("cast")
til metoden (og vent indtil dette bliver rettet af kompilatorudviklerne).
Problemet er, at de to grene af den ternære operatør returnerer forskellige typer.
Hvad med dette:
System.out.println(
String.valueOf(
true ? (Object)'a' : fail()
)
);
Eksplicit boksning af karakteren er én mulighed:
class Code {
public static void main(String[] args) throws Throwable {
System.out.println(
String.valueOf(
true ? Character.valueOf('a') : fail()
)
);
}
private static <R> R fail() {
throw null;
}
}