I'll start by saying I am a Java (/programming) newbie and this is my first question on the website.
Just learnt how to make an ordered list in Java using recursive nodes. Everything was easy until I ran into this exercise asking me to write a method that would double whatever value is contained in each node. Here is the code I tried to write:
public class ListaInteri<E extends Integer>
{
private NodoLista inizio;
// Private inner class each instance of these has a raw type variable and a
// ref
// to next node in the list
private class NodoLista
{
E dato;
NodoLista pros;
}
// method that adds whatever is meant by x to the begging of the list
public void aggiungi(E x)
{
NodoLista nodo = new NodoLista();
nodo.dato = x;
if (inizio != null)
nodo.pros = inizio;
inizio = nodo;
}
// a method that switches last and first elements in the list
public void scambia()
{
E datoFine;
if (inizio != null && inizio.pros != null) {
E datoInizio = inizio.dato;
NodoLista nl = inizio;
while (nl.pros != null)
nl = nl.pros;
datoFine = nl.dato;
inizio.dato = datoFine;
nl.dato = datoInizio;
}
}
// and here is the problem
// this method is supposed to double the value of the raw type variable dato
// of each node
public void raddoppia()
{
if (inizio != null) {
NodoLista temp = inizio;
while (temp != null) {
temp.dato *= 2;
}
}
}
开发者_运维问答// Overriding toString from object (ignore this)
public String toString(String separatore)
{
String stringa = "";
if (inizio != null) {
stringa += inizio.dato.toString();
for (NodoLista nl = inizio.pros; nl != null; nl = nl.pros) {
stringa += separatore + nl.dato.toString();
}
}
return stringa;
}
public String toString()
{
return this.toString(" ");
}
}
and here is the error the compiler gives me.
ListaInteri.java:39: inconvertible types
found : int
required: E
temp.dato*=2;
^
1 error
Now keep in mind any kind of help would be greatly appreciated anyway here are the questions I would like an answer to.
- Why does this happen? Isn't there such a thing as type erasure of raw types during compile time where all info that has to do with parameters or arguments types is ignored?
- How do I fix this? The second method ( the one that switches first and last) shows it's actually ok for the compiler to change fields in nodes as long as it is passed by another raw type, while if we try to multiply it by 2 for example it's no longer ok because compiler now knows we are talking about an int/Integer so gives this error in return. Thanks in advance for any answers.
EDIT; sorry had to make it readable should be ok now. EDIT2: almost readable argh!
Integer
is final, it cannot be extended. Therefore, your use of Generics is pointless (though syntactically valid), and you might as well just use int
- the problem will then disappear.
Type erasure is not the issue here (generic types are erased so they are not available at runtime, but at compile time, the types very much have to be convertible).
I would probably fix this to not use a type parameter at all. Integer
is a final class, so there is no point making it use a more generic type such as E extends Integer
.
One clarification, just to make sure you understand something: You asked,
Isn't there such a thing as type erasure of raw types during compile time where all info that has to do with parameters or arguments types is ignored?
People talk a lot about "type erasure" in Java, partially because type erasure is one of the many interesting differences between Java and C++ and partially because type erasure can give you strange and unexpected errors when you mix newer (Java 5 and later) with older Java code.
However, type erasure doesn't mean "all info that has to do with parameters or arguments types is ignored" - It means something much narrower than that. Also, as you said, type erasure is applied "during compile time", but it is applied after the compiler checks all the types and declarations in your program to see if there are any type errors.
First question: What is type erasure? Type erasure is something that only happens to generic types. Integer
is not a generic type, but ArrayList<T>
is a generic type, because you can declare an ArrayList<Integer>
or ArrayList<String>
. After the compiler is done checking the types in your program to make they all match, then it discards all of the information about generic types, so that an ArrayList<Integer>
and an ArrayList<String>
both just become ArrayList
. This is the reason why you can write if (foo instanceof String)
in Java, but you can't write if (foo instanceof ArrayList<String>)
: When that if
statement is evaluated at runtime, there is no way to tell an ArrayList<String>
apart from an ArrayList<Integer>
or from any other type of ArrayList
.
Second question: When is type erasure applied? Type erasure is applied when the compiler generates the compiled code (the "bytecode"), after the code is checked for compile errors. This means that the following code gives a compile error in Java, even though Java uses type erasure:
ArrayList<String> foo = new ArrayList<String>();
foo.add(new Integer(3)); // Error - Compiler knows that String is expected
I know I haven't answered your question, but I wanted to make sure you understood type erasure first. :-)
Getting back to your original question: Why does the class need to be declared with <E extends Integer>
? Is that specifically required for the homework assignment? I just copied and pasted your code into Eclipse, removed the <E extends Integer>
at the top, and replaced all of the E
declarations with Integer
, and it compiled just fine.
(By the way, there's still a problem with raddoppia
as you've written it here, even if you get rid of <E extends Integer>
and get it to compile - Have you found it yet? Try testing raddoppia
with a list that has more than one element, and you should see the problem...)
try :
temp.dato= new Integer(((Integer)temp.dato).intValue*2);
I think it should be
<E extends Number>
then you can say
tmp.dato = tmp.dato.intValue() * 2;
I think this will compile, but if someone uses your data structure for non-integers, this method won't produce the correct answer.
精彩评论