[Java] Unsafe/unchecked warning bij ArrayList

Pagina: 1
Acties:

  • GigaTexel_BE
  • Registratie: April 2002
  • Laatst online: 14:15
Sinds JDK 1.5 kan je niet meer zomaar arraylists aanmaken maar moet je ze declareren als volgt:

code:
1
2
private HashMap<String,IItem>cataloog;
HashMap<String,IItem> cataloog = (HashMap<String,IItem>)invoer.readObject();


Deze syntax vond ik op google
bij compilatie krijg ik echter:

code:
1
2
3
4
5
6
7
8
9
be\hogent\iii\bibliotheek\implementatie\Catalogus.java:89: warning: [unchecked]
unchecked cast
found   : java.lang.Object
required: java.util.HashMap<java.lang.String,be.hogent.iii.bibliotheek.IItem>
                        HashMap<String,IItem> cataloog = (HashMap<String,IItem>)
invoer.readObject();

                 ^
1 warning

Siesteem Spekkies!


  • Alarmnummer
  • Registratie: Juli 2001
  • Laatst online: 09-07-2024

Alarmnummer

-= Tja =-

Hij geeft een warning dat jij een Object cast naar een specifieker type.. Daar is niets mis mee.

  • GigaTexel_BE
  • Registratie: April 2002
  • Laatst online: 14:15
hier maakt hij dan weer helemaal geen probleem van:

code:
1
2
FileSystem temp = null;
temp = (FileSystem)invoer.readObject();

Siesteem Spekkies!


  • Alarmnummer
  • Registratie: Juli 2001
  • Laatst online: 09-07-2024

Alarmnummer

-= Tja =-

Algemene opmerking:
Waarom maak jij eerst een var aan en zet je die op null, en ga je die vervolgens zetten?

Leer dit aub zo snel mogelijk af.. extreem slechte programmeerstijl...

ipv:
code:
1
2
FileSystem temp = null;
temp = (FileSystem)invoer.readObject();


Gewoon:
code:
1
FileSystem temp = (FileSystem)invoer.readObject();

[ Voor 33% gewijzigd door Alarmnummer op 26-04-2005 12:14 ]


  • GambitRS
  • Registratie: Juni 2001
  • Laatst online: 13-06-2013

GambitRS

w00t

code:
1
private HashMap<String,IItem>cataloog = (HashMap<String,IItem>)invoer.readObject();

Dat kan niet meer?
Of anders:
code:
1
2
private HashMap<String,IItem>cataloog;
cataloog = (HashMap<String,IItem>)invoer.readObject();

of kan dat ook niet meer?

[ Voor 8% gewijzigd door GambitRS op 26-04-2005 12:14 ]

MechWarrior || Monsters Game


  • Soultaker
  • Registratie: September 2000
  • Laatst online: 02-05 01:32
Alarmnummer schreef op dinsdag 26 april 2005 @ 11:38:
Hij geeft een warning dat jij een Object cast naar een specifieker type.. Daar is niets mis mee.
Hoe zou je de code dan moeten schrijven dat je die warning niet krijgt? Het hele idee van een warning is dat je code mogelijk een ongewenst effect heeft en dat er een manier is om de code te herschrijven zo dat de bedoeling duidelijker is, en je de warning niet meer krijgt.

Maar die cast lijkt me hier gewoon heel duidelijk; wat verwacht de compiler dan? Dat je expliciet met instanceof eerst gaat kijken of het object wel van het juiste type is? Dat lijkt me nogal onzinnig, omdat het kenmerk van een cast juist is dat je je eerst af moet vragen of het object wel van het juiste type is, en het feit dat er geen instanceof bijstaat suggereert dat de programmeur wéét wat het type van het object is of bewust kiest voor een exception als dat anders is.

Die enkele regel code herschrijven naar:
Java:
1
2
3
4
5
Object obj = invoer.readObject();
if(obj instanceof HashMap<String,IItem>)
    cataloog = (HashMap<String,IItem>)obj;
else
    throw new ClassCastException();
alleen maar om de compiler tevreden te houden lijkt me een heel slecht idee.

  • ronaldmathies
  • Registratie: Juni 2001
  • Niet online
Wat is het 'invoer' object? Die moet namelijk ook een checked HashMap terug geven:

Bijvoorbeeld :

public HashMap<String,IItem> readObject() {
...
}

Anders hou je deze melding.

3015 Wp-z 5360 Wp-nno op 2 x SMA-SB3600 TL-21, Warmtepomp: ERSC-VM2CR2 / PUHZ-SHW140 YHA, WTW Q350, EV Kia Ev6 GT-Line


  • Alarmnummer
  • Registratie: Juli 2001
  • Laatst online: 09-07-2024

Alarmnummer

-= Tja =-

Soultaker schreef op dinsdag 26 april 2005 @ 12:34:
[...]

Hoe zou je de code dan moeten schrijven dat je die warning niet krijgt?
1) je zou de stream kunnen parametriseren ;)
2) niet :P Warning negeren.
Het hele idee van een warning is dat je code mogelijk een ongewenst effect heeft en dat er een manier is om de code te herschrijven zo dat de bedoeling duidelijker is, en je de warning niet meer krijgt.
Soms niet mogelijk.

  • Standeman
  • Registratie: November 2000
  • Laatst online: 23:08

Standeman

Prutser 1e klasse

javac -nowarn foo.java :+

The ships hung in the sky in much the same way that bricks don’t.


  • GigaTexel_BE
  • Registratie: April 2002
  • Laatst online: 14:15
Alarmnummer schreef op dinsdag 26 april 2005 @ 12:13:
Algemene opmerking:
Waarom maak jij eerst een var aan en zet je die op null, en ga je die vervolgens zetten?

Leer dit aub zo snel mogelijk af.. extreem slechte programmeerstijl...
komt omdat ik copy-paste vanuit verscheidene delen van de code, ik wou er voor de duidelijkheid de declaratie van het attribuut ook bijzetten
die null-instantiering heeft te maken met al dan niet gebruik maken van constructor enzo


hoe parameteriseer je die readObject()? er is namelijk geen readObject(Class klassenaam) of iets dergelijks :)

Siesteem Spekkies!


  • Alarmnummer
  • Registratie: Juli 2001
  • Laatst online: 09-07-2024

Alarmnummer

-= Tja =-

GigaTexel_BE schreef op dinsdag 26 april 2005 @ 14:56:
[...]

komt omdat ik copy-paste vanuit verscheidene delen van de code, ik wou er voor de duidelijkheid de declaratie van het attribuut ook bijzetten
die null-instantiering heeft te maken met al dan niet gebruik maken van constructor enzo
Stop ermee ;)
hoe parameteriseer je die readObject()? er is namelijk geen readObject(Class klassenaam) of iets dergelijks :)
Dat gaat slecht, vandaar mijn ;)

En parametriseren kan je doen met:

code:
1
2
InputStream<Persoon> pis = ...;
Persoon p = pis.readObject();


Maar moet je stream wel geparametriseerd kunnen worden + je moet weten dat uit een stream altijd 1 type Object terug komt.

  • GigaTexel_BE
  • Registratie: April 2002
  • Laatst online: 14:15
is me wel duidelijk, behalve bij ArrayLists
zal aan mij liggen dan :)

warning negeren dus, al is het met tegenzin

Siesteem Spekkies!


  • RooN
  • Registratie: Januari 2003
  • Laatst online: 01-04 20:52
Die haken die nieuw zijn in v1.5 (< >) zijn generics en erg fijn te gebruiken. Dit maakt in sommige gevallen het type-casten niet meer nodig. Laat me dit aan de hand van het volgende voorbeeld duidelijk maken:

Zo ging het voorheen, bij het toevoegen van de waarden 1, 2, 3 en 4 moet je erbij declareren dat ze van het object Integer zijn:
JavaScript:
1
2
3
4
5
6
Collection A = new TreeSet();
Collection B = new Vector();
A.add(new Integer(1));
A.add(new Integer(2));
B.add(new Integer(3));
B.add(new Integer(4));

Met generics kan het anders:
JavaScript:
1
2
3
4
5
6
Collection<Integer> A = new TreeSet<Integer>();
Collection<Integer> B = new Vector<Integer>();
A.add(1);
A.add(2);
B.add(3); 
B.add(4);

Omdat je bij het declareren van de collecties dmv de generics al aangeeft wat erin moeten komen (Integers), zal de add-operatie niet gaan zeuren maar er altijd Integers van maken (dit heet autoboxing).

Of dit in jou geval dus ook van toepassing is weet ik zo niet, maar wat gebeurd er als je invoer.readObject(); niet cast?

smile an everlasting smile


Verwijderd

De oplossing is heel 'simpel': die warning moet je negeren, er bestaat geen oplossing voor.

Waarom? Sinds versie 1.5 kan je inderdaad classes parametriseren. In principe kan je de readObject() functie dus zo maken dat hij een type (zeg E) returnt, ipv. Object. Maar om backwards compatibility met vorige versies te vrijwaren (toen kon je dus nog niet parametriseren), heeft Sun ervoor gekozen om readObject nog altijd een een Object ipv. een type te laten returnen.

http://java.sun.com/j2se/...ava/io/Serializable.html:
Classes that require special handling during the serialization and deserialization process must implement special methods with these exact signatures:

private void writeObject(java.io.ObjectOutputStream out)
throws IOException
private void readObject(java.io.ObjectInputStream in)
throws IOException, ClassNotFoundException;
Met andere woorden: je kan er niets aan doen.

Als je me niet gelooft, kan je altijd eens in de source van eender welke collection implementatie (bijv. ArrayList) gaan kijken, daar casten ze ook naar het betreffende type (ArrayList<E>).

  • mbravenboer
  • Registratie: Januari 2000
  • Laatst online: 06-11-2025
Inderdaad is er niet echt een oplossing voor. Het doen van IO zorgt altijd voor zulke problemen. Uiteraard moet je in alle andere gevallen wel proberen om zulke casts en de bijbehorende waarschuwingen te voorkomen!

Als de waarschuwing je echt soort, kan je in de laatste builds van Mustang SuppressWarnings gebruiken:
Java:
1
2
3
4
5
6
7
8
9
10
11
12
import java.util.HashMap;

class Foo
{
  Object x;

  @SuppressWarnings("unchecked")
  void bar()
  {
    HashMap<String, Integer> catalog = (HashMap<String,Integer>) x;
  }
}

Helaas wordt dit nog niet gesupport in de eerste releases van 1.5.0, alhoewel het al wel beschreven is in de standaard en SuppressWarnings ook bestaat.

Blog, Stratego/XT: Program Transformation, SDF: Syntax Definition, Nix: Software Deployment


  • misfire
  • Registratie: Maart 2001
  • Laatst online: 12-10-2024
Wellicht is het je al duidelijk, maar in de andere reacties wordt volgens mij niet specifiek uitgelegd waarom de Java compiler dit wel zonder warning toestaat:
code:
1
String tekst = (String) invoer.readObject();
en dit vervolgens "unchecked" vindt:
code:
1
HashMap<String,IItem> cataloog = (HashMap<String,IItem>)invoer.readObject();
De warning is niet om te vertellen dat je runtime een ClassCastException zou kunnen krijgen. De warning is juist om je te waarschuwen dat dit niet altijd een CCE oplevert, ook al heb je het verkeerde type! Dit heeft te maken met het "erasure" gedrag van Java Generics: at runtime is de Generics informatie niet meer beschikbaar, en zal er alleen op het niet-geparametriseerde gedeelte gecontroleerd worden door de runtime. Dat betekent dat als je object input stream bijvoorbeeld een HashMap<Integer, IItem> zou bevatten, dit gewoon door de cast zou gaan. Andere gedeeltes van je code zouden dit echter niet zo prettig vinden, waardoor je op bizarre plekken CCE's zou kunnen krijgen.

Mocht je gebruik maken van de Eclipse IDE, die een eigen compiler heeft, dan krijg je ten eerste een betere warning:
Type safety: The cast from Object to HashMap<String,IItem> is actually checking against the erased type HashMap
Ten tweede kun je in de opties van de Eclipse Java compiler specifiek dit type warning uitschakelen. De annotation oplossing is het mooist maar wordt helaas ook in Eclipse nog niet herkend.

  • misfire
  • Registratie: Maart 2001
  • Laatst online: 12-10-2024
Misschien nog wel twee aardige updates op dit verhaal:

1. In Eclipse 3.1 M7 wordt @SuppressWarnings nu ook herkend, dus je kunt dit nu ook in Eclipse gebruiken.

2. Als je zelf invloed hebt op de objecten die in de serialisatiestroom terecht komen dan kun je voor dit specifieke geval ook een wrapper class maken voor je generic types. De wrapper class informatie blijft wel tijdens runtime behouden, dus je kunt dan volledig veilig casten. Bijvoorbeeld zo:

code:
1
2
3
4
5
6
7
8
9
    public class FooBar extends ArrayList<String> {
        private static final long serialVersionUID = 1L;
    }

    public void doeIets() throws IOException, ClassNotFoundException {
        ObjectInputStream invoer = new ObjectInputStream(new FileInputStream("bla"));
        FooBar foobar = (FooBar) invoer.readObject();
        List<String> lijst = foobar;
    }

[ Voor 3% gewijzigd door misfire op 17-05-2005 19:57 ]

Pagina: 1