En introduktion til generiske typer i Java: kovarians og kontrast

Typer
Java er et statisk skrevet sprog, hvilket betyder, at du først skal erklære en variabel og dens type, før du bruger den.
For eksempel: int myInteger = 42;
Indtast generiske typer.
Generiske typer
Definition: "En generisk type er en generisk klasse eller grænseflade, der er parametreret over typer."
I det væsentlige giver generiske typer dig mulighed for at skrive en generel, generisk klasse (eller metode), der fungerer med forskellige typer, hvilket giver mulighed for genbrug af kode.
I stedet for at angive obj
at være af en int
type eller en String
type eller en hvilken som helst anden type, definerer du Box
klassen til at acceptere en typeparameter <
; T>. Derefter kan du n
bruge T til at repræsentere den generiske type i en hvilken som helst del inden for din klasse.
Indtast nu kovarians og kontrast.
Kovarians og kontravarians
Definition
Varians henviser til, hvordan undertypning mellem mere komplekse typer relaterer til undertypning mellem deres komponenter (kilde).
En let at huske (og ekstremt uformel) definition af kovarians og kontravarans er:
- Kovarians: accepter undertyper
- Kontrast: accepter supertyper
Arrays
I Java er arrays kovariante , hvilket har to implikationer.
For det første kan en matrix af typen T[]
indeholde elementer af typen T
og dens undertyper.
Number[] nums = new Number[5];nums[0] = new Integer(1); // Oknums[1] = new Double(2.0); // Ok
For det andet er en matrix af typen S[]
en undertype af T[]
hvis S
er en undertype af T
.
Integer[] intArr = new Integer[5];Number[] numArr = intArr; // Ok
Det er dog vigtigt at huske, at: (1) numArr
er en reference til referencetypen Number[]
til det "faktiske objekt" intArr
af "faktiske type" Integer[]
.
Derfor vil følgende linje kompilere fint, men producere en runtime ArrayStoreException
(på grund af forurening af dynger):
numArr[0] = 1.23; // Not ok
Det producerer en runtime-undtagelse, fordi Java ved runtime ved, at det "faktiske objekt" intArr
faktisk er en matrix af Integer
.
Generiske stoffer
Med generiske typer har Java ingen måde at kende typeoplysningerne om typeparametre på runtime på grund af sletning af typen. Derfor kan den ikke beskytte mod dyngeforurening ved kørsel.
Som sådan er generiske stoffer uforanderlige.
ArrayList intArrList = new ArrayList();ArrayList numArrList = intArrList; // Not okArrayList anotherIntArrList = intArrList; // Ok
Typeparametrene skal matche nøjagtigt for at beskytte mod dyngeforurening.
Men indtast wildcards.
Jokertegn, kovarians og kontrast
Med jokertegn er det muligt for generiske stoffer at understøtte kovarians og kontrast.
Tilpasning af det foregående eksempel får vi dette, hvilket fungerer!
ArrayList intArrList = new ArrayList();ArrayList numArrList = intArrList; // Ok
Spørgsmålstegnet “?” henviser til et jokertegn, der repræsenterer en ukendt type. Det kan være lavere afgrænset, hvilket begrænser den ukendte type til at være en bestemt type eller dens supertype.
Derfor, i linje 2, ? super Integer
oversættes til "enhver type, der er en heltalstype eller dens supertype".
Du kunne også begrænse wildcardet, som begrænser den ukendte type til at være en bestemt type eller dens undertype ved hjælp af ? extends Integer
.
Skrivebeskyttet og skrivebeskyttet
Kovarians og kontravarans giver nogle interessante resultater. Kovariante typer er skrivebeskyttede, mens modstridende typer er skrivebeskyttede.
Husk, at covariant-typer accepterer undertyper, så ArrayList
er> can contain any object that is either
of a
Number type or its subtype.
In this example, line 9 works, because we can be certain that whatever we get from the ArrayList can be upcasted to a
Number
type (because if it extends Number
, by definition, it is a Number
).
But
nums.add()
doesn’t work, because we cannot be sure of the “actual type” of the object. All we know is that it must be a Number
or its subtypes (e.g. Integer, Double, Long, etc.).
With contravariance, the converse is true.
Line 9 works, because we can be certain that whatever the “actual type” of the object is, it must be
Integer
or its supertype, and thus accept an Integer
object.
But line 10 doesn’t work, because we cannot be sure that we will get an
Integer
. For instance, nums
could be referencing an ArrayList of Objects
.
Applications
Therefore, since covariant types are read-only and contravariant types are write-only (loosely speaking), we can derive the following rule of thumb: “Producer extends, consumer super”.
A producer-like object that produces objects of type
T
can be of type parameter
T>, while a consumer-like object that consumes objects of
type T can be of type para
meter
super T>.