Java - изменение значения переменной необработанного типа. Возможный? - PullRequest
3 голосов
/ 20 февраля 2010

Начну с того, что я новичок в Java (/ программировании), и это мой первый вопрос на сайте.

Только что научился составлять упорядоченный список в Java с использованием рекурсивных узлов. Все было легко, пока я не приступил к этому упражнению и не попросил меня написать метод, который удваивал бы любое значение, содержащееся в каждом узле. Вот код, который я пытался написать:

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(" ");
    }

}

и вот ошибка, которую выдаёт мне компилятор.

    ListaInteri.java:39: inconvertible types
found   : int
required: E
  temp.dato*=2;         
             ^
1 error

Теперь имейте в виду, что любая помощь будет принята с благодарностью. Вот вопросы, на которые я хотел бы получить ответ.

  1. Почему это происходит? Разве не существует такой вещи, как стирание типов необработанных типов во время компиляции, когда вся информация, связанная с параметрами или типами аргументов, игнорируется?
  2. Как мне это исправить? Второй метод (тот, который переключает первый и последний) показывает, что компилятор может изменять поля в узлах, если он передается другим необработанным типом, например, если мы попытаемся умножить его на 2, например, это уже не нормально. поскольку компилятор теперь знает, что мы говорим о int / Integer, он возвращает эту ошибку в ответ. Заранее спасибо за любые ответы.

EDIT; извините пришлось сделать его читабельным должно быть в порядке сейчас. РЕДАКТИРОВАТЬ 2: почти читаемый Argh!

Ответы [ 5 ]

6 голосов
/ 20 февраля 2010

Integer является окончательным, его нельзя продлить. Следовательно, использование вами Generics бессмысленно (хотя и синтаксически допустимо), и вы можете просто использовать int - проблема исчезнет.

3 голосов
/ 20 февраля 2010

Стирание типов здесь не является проблемой (универсальные типы стираются, поэтому они недоступны во время выполнения, но во время компиляции типы должны быть преобразованы).

Возможно, я бы исправил это, чтобы вообще не использовать параметр типа. Integer является последним классом, поэтому нет смысла использовать более общий тип, такой как E extends Integer.

1 голос
/ 22 февраля 2010

Одно уточнение, просто чтобы убедиться, что вы что-то понимаете: Вы спросили,

Нет ли такой вещи, как тип стирание необработанных типов во время компиляции время, когда вся информация, которая должна сделать с параметрами или типами аргументов игнорировали?

Люди много говорят о «стирании типов» в Java, частично потому, что стирание типов является одним из многих интересных отличий между Java и C ++, а частично потому, что стирание типов может привести к странным и неожиданным ошибкам при смешивании новых (Java 5 позже) со старым кодом Java.

Однако стирание типов не означает «вся информация, связанная с параметрами или типами аргументов, игнорируется» - это означает нечто гораздо более узкое, чем это. Также, как вы сказали, стирание типов применяется «во время компиляции», но оно применяется после , компилятор проверяет все типы и объявления в вашей программе, чтобы увидеть, есть ли какие-либо ошибки типов.

Первый вопрос: что такое стирание типа? Стирание типов - это то, что происходит только с общими типами. Integer не является универсальным типом, но ArrayList<T> является универсальным типом, потому что вы можете объявить ArrayList<Integer> или ArrayList<String>. После того, как компилятор завершит проверку типов в вашей программе, чтобы они все совпадали, он отбрасывает всю информацию об универсальных типах, так что ArrayList<Integer> и ArrayList<String> оба просто становятся ArrayList. По этой причине вы можете написать if (foo instanceof String) на Java, но вы не можете написать if (foo instanceof ArrayList<String>): когда этот оператор if вычисляется во время выполнения, невозможно отличить ArrayList<String> от ArrayList<Integer> или из любого другого типа ArrayList.

Второй вопрос: когда применяется стирание типа? Стирание типа применяется, когда компилятор генерирует скомпилированный код («байт-код») после проверки кода на наличие ошибок компиляции. Это означает, что следующий код выдает ошибку компиляции в Java, хотя Java использует стирание типа:

ArrayList<String> foo = new ArrayList<String>();
foo.add(new Integer(3)); // Error - Compiler knows that String is expected

Я знаю, что не ответил на ваш вопрос, но я хотел убедиться, что вы сначала поняли стирание типа. : -)

Возвращаясь к исходному вопросу: почему класс должен быть объявлен с <E extends Integer>? Это специально требуется для домашнего задания? Я просто скопировал и вставил ваш код в Eclipse, удалил <E extends Integer> сверху и заменил все объявления E на Integer, и он скомпилировался просто отлично.

(Кстати, по-прежнему проблема с raddoppia, как вы написали здесь, даже если вы избавились от <E extends Integer> и получили его для компиляции - вы уже нашли его? Попробуйте протестировать raddoppia со списком, содержащим более одного элемента, и вы должны увидеть проблему ...)

0 голосов
/ 20 февраля 2010

Я думаю, что это должно быть

<E extends Number>

тогда вы можете сказать

tmp.dato = tmp.dato.intValue() * 2;

Я думаю, что это скомпилируется, но если кто-то использует вашу структуру данных для нецелых, этот метод не даст правильный ответ.

0 голосов
/ 20 февраля 2010

попробуй:

  temp.dato= new Integer(((Integer)temp.dato).intValue*2);
...