Трудности, которые понимают метод потока toArray в java 8 [duplicate]

Объявлено, но не определено переменная или функция.

Типичным объявлением переменной является

extern int x;

. Поскольку это только объявление, требуется одно определение. Соответствующим определением будет:

int x;

Например, следующее генерирует ошибку:

extern int x;
int main()
{
    x = 0;
}
//int x; // uncomment this line for successful definition

Аналогичные замечания относятся к функциям. Объявление функции без ее определения приводит к ошибке:

void foo(); // declaration only
int main()
{
   foo();
}
//void foo() {} //uncomment this line for successful definition

Будьте осторожны, чтобы выполняемая вами функция точно соответствовала той, которую вы объявили. Например, у вас могут быть несогласованные cv-квалификаторы:

void foo(int& x);
int main()
{
   int x;
   foo(x);
}
void foo(const int& x) {} //different function, doesn't provide a definition
                          //for void foo(int& x)

Другие примеры несоответствий включают

  • Функция / переменная, объявленная в одном пространстве имен, определенное в другом.
  • Функция / переменная, объявленная как член класса, определяемая как глобальная (или наоборот).
  • Тип возвращаемого значения функции, номер и типы параметров и соглашение о вызове не совсем точно согласуются.

Сообщение об ошибке из компилятора часто дает вам полное объявление переменной или функции, которая была объявлена, но не определена. Сравните его с определением, которое вы указали. Убедитесь, что каждая деталь соответствует.

48
задан Paul Bellora 19 August 2013 в 04:18
поделиться

4 ответа

super для привязки параметра именованного типа (например, <S super T>) в отличие от подстановочного знака (например, <? super T>) НЕЗАКОННО просто потому, что даже если это разрешено, оно не будет делать то, что вы надеялись do, потому что поскольку Object является конечной super всех ссылочных типов, и все является Object, , в действительности нет привязки .

В вашем конкретном например, поскольку любой массив ссылочного типа является Object[] (по ковариации Java-массива), поэтому он может использоваться как аргумент <S super T> S[] toArray(S[] a) (если такая привязка является законной) во время компиляции , и это не помешало бы ArrayStoreException во время выполнения.

То, что вы пытаетесь предложить, заключается в том, что данный:

List<Integer> integerList;

и с учетом этого гипотетического super, связанный с toArray:

<S super T> S[] toArray(S[] a) // hypothetical! currently illegal in Java

, компилятор должен разрешать следующее:

integerList.toArray(new Integer[0]) // works fine!
integerList.toArray(new Number[0])  // works fine!
integerList.toArray(new Object[0])  // works fine!

и другие аргументы типа массива ( поскольку Integer имеет только эти 3 типа в качестве super). То есть вы пытаетесь предотвратить это от компиляции:

integerList.toArray(new String[0])  // trying to prevent this from compiling

, потому что по вашему аргументу String не является super в Integer. Однако , Object является super в Integer, а String[] является Object[], поэтому компилятор все еще позволил бы скомпилировать выше, даже если гипотетически вы можете сделать <S super T>!

Таким образом, следующий все еще будет компилировать (так же, как и сейчас), а ArrayStoreException во время выполнения не препятствовать какой-либо проверке времени компиляции с использованием ограничений общего типа:

integerList.toArray(new String[0])  // compiles fine!
// throws ArrayStoreException at run-time

Дженерики и массивы не смешиваются, и это одно из многих мест, где оно показано.


Пример без массива

Опять же, скажем, что у вас есть это объявление универсального метода:

<T super Integer> void add(T number) // hypothetical! currently illegal in Java

И у вас есть эти объявления переменных:

Integer anInteger
Number aNumber
Object anObject
String aString

Ваше намерение с <T super Integer> (если оно законно) заключается в том, что оно должно разрешать add(anInteger) и add(aNumber), и, конечно, add(anObject), но NOT add(aString). String - Object, поэтому add(aString) все равно будет компилироваться.


См. Также

Связанные вопросы

В правилах типизации генериков:

При использовании super и extends:

48
ответ дан Community 28 August 2018 в 06:39
поделиться

«Официальный» ответ на ваш вопрос можно найти в отчете об ошибке Sun / Oracle .

BT2: ОЦЕНКА

См.

http://lampwww.epfl.ch/~odersky/ftp/local-ti.ps

, в частности раздел 3 и последний абзац на странице 9. Переменные типа допуска с обеих сторон ограничений подтипа могут приводить к набору уравнений типа без единого наилучшего решения; следовательно, вывод типа не может быть выполнен с использованием каких-либо существующих стандартных алгоритмов. Вот почему переменные типа имеют только «расширяющие» границы.

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

@ ###. ### 2004-05-25

Да; ключевым моментом является то, что подстановочные знаки, даже если они были захвачены, используются только в качестве входных данных процесса вывода; ничто с (только) нижняя граница должна быть выведена в результате.

@ ###. ### 2004-05-26

Я вижу проблему. Но я не вижу, как он отличается от проблем, которые мы имеем с нижними границами в подстановочных знаках во время вывода, например ::

List & lt ;? super Number & gt; s; boolean b; ... s = b? s: s;

В настоящее время мы выводим список & lt; X & gt; где X расширяет Object как тип условного выражения, что означает, что присвоение является незаконным.

@ ###. ### 2004-05-26

К сожалению, разговор заканчивается. Бумага, на которую ссылалась (теперь мертвая) ссылка, была Inferred Type Instantiation для GJ . От взгляда на последнюю страницу это сводится к: если допускаются нижние границы, вывод типа может давать несколько решений, ни один из которых main .

8
ответ дан Ben Schulz 28 August 2018 в 06:39
поделиться

Предположим, что у нас есть:

  • базовые классы A> B> C и D
    class A{
        void methodA(){}
    };
    class B extends  A{
        void methodB(){}
    }
    
    class C extends  B{
        void methodC(){}
    }
    
    class D {
        void methodD(){}
    }
    
  • классы оболочки работы
    interface Job<T> {
        void exec(T t);
    }
    
    class JobOnA implements Job<A>{
        @Override
        public void exec(A a) {
            a.methodA();
        }
    }
    class JobOnB implements Job<B>{
        @Override
        public void exec(B b) {
            b.methodB();
        }
    }
    
    class JobOnC implements Job<C>{
        @Override
        public void exec(C c) {
            c.methodC();
        }
    }
    
    class JobOnD implements Job<D>{
        @Override
        public void exec(D d) {
            d.methodD();
        }
    }
    
  • и один менеджерский класс с 4 различными подходами к выполнению задания на объекте
    class Manager<T>{
        final T t;
        Manager(T t){
            this.t=t;
        }
        public void execute1(Job<T> job){
            job.exec(t);
        }
    
        public <U> void execute2(Job<U> job){
            U u= (U) t;  //not safe
            job.exec(u);
        }
    
        public <U extends T> void execute3(Job<U> job){
            U u= (U) t; //not safe
            job.exec(u);
        }
    
        //desired feature, not compiled for now
        public <U super T> void execute4(Job<U> job){
            U u= (U) t; //safe
            job.exec(u);
        }
    }
    
  • с использованием
    void usage(){
        B b = new B();
        Manager<B> managerB = new Manager<>(b);
    
        //TOO STRICT
        managerB.execute1(new JobOnA());
        managerB.execute1(new JobOnB()); //compiled
        managerB.execute1(new JobOnC());
        managerB.execute1(new JobOnD());
    
        //TOO MUCH FREEDOM
        managerB.execute2(new JobOnA()); //compiled
        managerB.execute2(new JobOnB()); //compiled
        managerB.execute2(new JobOnC()); //compiled !!
        managerB.execute2(new JobOnD()); //compiled !!
    
        //NOT ADEQUATE RESTRICTIONS     
        managerB.execute3(new JobOnA());
        managerB.execute3(new JobOnB()); //compiled
        managerB.execute3(new JobOnC()); //compiled !!
        managerB.execute3(new JobOnD());
    
        //SHOULD BE
        managerB.execute4(new JobOnA());  //compiled
        managerB.execute4(new JobOnB());  //compiled
        managerB.execute4(new JobOnC());
        managerB.execute4(new JobOnD());
    }
    

Любые предложения по реализации execute4 сейчас?

========== edit =======

    public void execute4(Job<? super  T> job){
        job.exec( t);
    }

Спасибо всем:)

======= === edit ==========

    private <U> void execute2(Job<U> job){
        U u= (U) t;  //now it's safe
        job.exec(u);
    }
    public void execute4(Job<? super  T> job){
        execute2(job);
    }

гораздо лучше, любой код с U внутри execute2

супер тип U становится именованным!

интересное обсуждение:)

1
ответ дан nxdrvr 28 August 2018 в 06:39
поделиться

Поскольку никто не дал удовлетворительного ответа, правильный ответ кажется «из-за недостатка языка Java».

polygenelubricants предоставили хороший обзор плохих вещей, происходящих с ковариантностью Java-массива, которая это ужасная особенность сама по себе. Рассмотрим следующий фрагмент кода:

String[] strings = new String[1];
Object[] objects = strings;
objects[0] = 0;

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

Теперь, здесь у меня есть вполне допустимый пример кода, требующего super в параметре named type:

class Nullable<A> {
    private A value;
    // Does not compile!!
    public <B super A> B withDefault(B defaultValue) {
        return value == null ? defaultValue : value;
    }
}

Потенциально поддерживая некоторое приятное использование:

Nullable<Integer> intOrNull = ...;
Integer i = intOrNull.withDefault(8);
Number n = intOrNull.withDefault(3.5);
Object o = intOrNull.withDefault("What's so bad about a String here?");

Последний фрагмент кода не компилируется, если я вообще удаляю B, поэтому B действительно необходим.

Обратите внимание, что функция, которую я пытаюсь реализовать, легко получить, если я инвертирую порядок объявлений параметров типа, изменив ограничение super на extends. Однако это возможно только в том случае, если я переписываю метод как статический:

// This one actually works and I use it.
public static <B, A extends B> B withDefault(Nullable<A> nullable, B defaultValue) { ... }

Дело в том, что это ограничение языка Java действительно ограничивает некоторые возможные полезные функции и может потребовать уродливые обходные пути. Интересно, что произойдет, если нам понадобится withDefault, чтобы быть виртуальным.

Теперь, чтобы соотнести с тем, что говорят полигеномассы, мы используем B здесь, чтобы не ограничивать тип объекта, переданного как defaultValue ( см. строку, используемую в примере), а скорее ограничить ожидания вызывающего абонента возвратом объекта. В качестве простого правила вы используете extends с типами, которые вы требуете, и super с типами, которые вы предоставляете.

25
ответ дан Rotsor 28 August 2018 в 06:39
поделиться
Другие вопросы по тегам:

Похожие вопросы: