powered by simpleCommunicator - 2.0.61     © 2026 Programmizd 02
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Форумы / Java [игнор отключен] [закрыт для гостей] / Помогите с наследованием в генериках
12 сообщений из 12, страница 1 из 1
Помогите с наследованием в генериках
    #38997331
chabapok
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Вопрос такой. Какой тип должен быть у параметра, передаваемого в f(), при условии, что она может принимать ссылки на методы Foo и его наследников???

мне кажется, туда надо BiFunction<? extends Foo, String, Integer>, но компилятор не пускает. Подобрать методом перебора не вышло.

Код: java
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
public class Foo {
    int func(String v){ return 5;}  
}

public class FooChild1 extends Foo{
    @Override
    int func(String v){ return 10;}  
}

public class FooChild2 extends Foo{
    @Override
    int func(String v){ return 15;}  
}

public class Proba {
        

    public static void f( _какой_тут_нужен_тип?_ v ){
    }

    
    public static void main(String[] args){
        f( FooChild2::func );
        f( FooChild1::func );
        f( Foo::func );
    }
    
}
...
Рейтинг: 0 / 0
Помогите с наследованием в генериках
    #38997414
Фотография Usman
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
chabapok,

Код: java
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
public class FooClass {

    public static interface IFoo {
        
        int func(String v);
    }

    public static class Foo implements IFoo {
        
        @Override
        public int func(String v) {
            return 5;
        }
    }

    public static class FooChild1 extends Foo {
        
        @Override
        public int func(String v) {
            return 10;
        }
    }

    public static class FooChild2 extends Foo {
        @Override
        public int func(String v) {
            return 15;
        }
    }
    
    public static void func(IFoo v) {
    }
    
    public static void main(String[] args) {
        Foo f = new Foo();
        FooChild1 fc1 = new FooChild1();
        FooChild1 fc2 = new FooChild1();
        
        func(f::func);
        func(fc1::func);
        func(fc2::func);
    }
}
...
Рейтинг: 0 / 0
Помогите с наследованием в генериках
    #38997470
chabapok
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Не подходит.

1. Вы показали как сослаться на метод по объекту - но для этого нужен объект.
Мне интересен способ сослаться по классу, т.к. объект в момент ссылания неизвестен. В учебнике https://docs.oracle.com/javase/tutorial/java/javaOO/methodreferences.html то что вы показали названо "Reference to an instance method of a particular object", а меня интересует "Reference to an instance method of an arbitrary object of a particular type"

2. Тут наверное больше непонятка из за того, что я не полностью покрыл примером то что хочется. Функция f() должна принимать ссылки на методы Foo и его наследников. Это значит, что если у нас в классе FooChild1 есть новая функция с подходящей сигнатурой, на нее тоже нужно, чтобы можно было сослаться:

Код: java
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
public class FooChild1 extends Foo{
    @Override
    int func(String v){ return 10;}  

    int func2(String v){return 500;}
}

 public static void main(String[] args){
        f( FooChild1::func2 );
 }



ps. С ArrayList прокатывает такое:
...
Рейтинг: 0 / 0
Помогите с наследованием в генериках
    #38997480
chabapok
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
В предыдущем сообщении забыл запостить рассуждение про ArrayList. Канонический пример, если у нас есть ArrayList класса Foo или его наследников, выглядит как-то так:

Код: java
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
    public static void f1(ArrayList<? extends Foo> v){ }    
    
    public static void main(String[] args){
        
        ArrayList<FooChild1> fc = new ArrayList<FooChild1>();
        f1(fc);
        
        ArrayList<FooChild2> fc1 = new ArrayList<FooChild2>();
        f1(fc1);
        
        ArrayList<Foo> f = new ArrayList<Foo>();
        f1(f);



Вообще, есть две конструкции:
? extends Foo - дает право присваивать ссылки на ArrayList с учетом наследования, но запрещает класть в него объекты
? super Foo - дает право класть в массив объекты, но ссылки можно присваивать только те, где T идет выше по иреархии наследования.

Теперь посмотрим что у нас есть со ссылками на метод
Код: java
1.
2.
3.
4.
5.
6.
7.
8.
9.
        // Следущие конструкции компилятор пропускает нормально:
        BiFunction<FooChild1, String, Integer> bf1 = FooChild1::func;
        BiFunction<FooChild2, String, Integer> bf2 = FooChild2::func;
        BiFunction<Foo, String, Integer> bf3 = Foo::func;


        //но в то же время не компилит:
        BiFunction<? extends Foo, String, Integer> bf = FooChild1::func;
        BiFunction<? extends Foo, String, Integer> bf2 = Foo::func;



Возможно, нужно комбинировать у Function второй и третий параметр как ? extends (или super) String - но у меня с этим тоже ничего не вышло.
...
Рейтинг: 0 / 0
Помогите с наследованием в генериках
    #38997488
chabapok
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Вообще, дело явно в первом параметре. Такое впечатление, что компилятор не поддерживает наследования в ссылках на метод.
Я чуток упростил пример:

Код: java
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
import java.util.function.Consumer;
import java.util.function.Supplier;

public class Foo {

    public static void mian(String [] arg){
        //тут компилится
        Consumer<Bar> f = Bar::myFunc;
        Consumer<Bar1> f1 = Bar1::myFunc;
        
        //тут не компилится
        Consumer<? super Bar> f2 = Bar1::myFunc;
        Consumer<? extends Bar> f3 = Bar1::myFunc; //вот тут должно бы компилиться по идее
        Consumer<Bar> f4 = Bar1::myFunc;

        // неговоря уже про func1
        Consumer<? super Bar> f5 = Bar1::myFunc1;
        Consumer<? extends Bar> f6 = Bar1::myFunc1;  // вот тут должно бы компилиться по идее
        Consumer<Bar> f7 = Bar1::myFunc1;
    }
    
}

class Bar {    
    void myFunc(){ }
}

class Bar1 extends Bar{
    void myFunc(){ }
    void myFunc1(){  }
}
...
Рейтинг: 0 / 0
Помогите с наследованием в генериках
    #38998261
Фотография Диез
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
chabapok,

Ну и вопросы у вас.. Задумываться приходится :)

Тут, как бы, проблема не просто в method references, а глубже, в вариантности дженериков.

http://stackoverflow.com/questions/2723397/java-generics-what-is-pecs

Если "развернуть" ссылку на метод, получится что-то типа такого:

Код: java
1.
2.
3.
4.
5.
6.
7.
8.
		Consumer<? extends Bar> c1 = new Consumer<Bar1>() {

			public void accept(Bar1 t) {
				t.myFunc1();  
			}
		}; // это даже компилируется

		c1.accept(new Bar()); // и что тут делать??



Consumer - контравариантный тип, и требует использования супертипа, а не потомка.
Вот так прокатит:

Код: java
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
class UnderBar {

	public void myFunc() {
		System.out.println("UnderBar");
	}

}

class Bar extends UnderBar {

	@Override
	public void myFunc() {
		System.out.println("Bar");
	}

}

...

Consumer<? super Bar> c0 = UnderBar::myFunc; 
c0.accept(new Bar());
...
Рейтинг: 0 / 0
Помогите с наследованием в генериках
    #38998402
chabapok
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
ДиезConsumer - контравариантный тип, и требует использования супертипа, а не потомка.
Такого понятия не существует в java.
В какую сторону требуется наследование - определяется словом super или extends.


К вашему примеру я приходил методом тыка. Это немножко не то, по той причине, что такой способ позволяет идти по ветке наследования вверх. Это относительно бесполезная фишка для Consumer-ов, она предназначена для контейнеров. В Consumer можно писать без super. Следующий пример компилится:

Код: java
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
class Bar {    
    void myFunc(){ }
}

class Bar1 extends Bar{
    void myFunc(){ }
    void myFunc1(){  }
}

        Consumer<? super Bar1> f2 = Bar1::myFunc;
        f2 = Bar1::myFunc1;
        f2 = Bar::myFunc;
        f2 = Object::notify;
        
        //теперь то же без super
        Consumer<Bar1> f3 = Bar1::myFunc1;
        Consumer<Bar1> f4 = Bar1::myFunc;
        f2 = Bar::myFunc;
        f2 = Object::notify;
        



В контейнерах все хитрей, есть урок на русском по этой теме тут
YouTube Video
...
Рейтинг: 0 / 0
Помогите с наследованием в генериках
    #38998624
Фотография Диез
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
chabapokДиезConsumer - контравариантный тип, и требует использования супертипа, а не потомка.
Такого понятия не существует в java.
В какую сторону требуется наследование - определяется словом super или extends.

Да, в Java нет явного указания, что параметр типа является ко- или контравариантным (как в Scala или c#).
Но вариантность в широком смысле - это поведение типа, а не фича языка
https://ru.wikipedia.org/wiki/Ковариантность_и_контравариантность_(программирование)#Java

chabapokК вашему примеру я приходил методом тыка. Это немножко не то, по той причине, что такой способ позволяет идти по ветке наследования вверх. Это относительно бесполезная фишка для Consumer-ов, она предназначена для контейнеров. В Consumer можно писать без super. Следующий пример компилится:

Код: java
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
class Bar {    
    void myFunc(){ }
}

class Bar1 extends Bar{
    void myFunc(){ }
    void myFunc1(){  }
}

        Consumer<? super Bar1> f2 = Bar1::myFunc;
        f2 = Bar1::myFunc1;
        f2 = Bar::myFunc;
        f2 = Object::notify;
        
        //теперь то же без super
        Consumer<Bar1> f3 = Bar1::myFunc1;
        Consumer<Bar1> f4 = Bar1::myFunc;
        f2 = Bar::myFunc;
        f2 = Object::notify;
        



В контейнерах все хитрей, есть урок на русском по этой теме тут
...

Так что логично было бы предположить, что extends позволить идти по ветке наследования вниз. Но нет, со ссылками на метод это не работает.

Принципиальной разницы между контейнерами и функциональными интерфейсами я не вижу - поведение generic-типов одинаковое.

К Consumer<T> есть парный интерфейс Supplier<T> - он-то как раз позволяет использовать подклассы для параметризации:

Код: java
1.
2.
3.
4.
5.
6.
7.
8.
		Supplier<? extends Bar> s1 = new Supplier<Bar1>() {
			@Override
			public Bar1 get() {
				return new Bar1();
			}
		};

		Bar res = s1.get(); // всё ОК



Рассматривайте Supplier как IN-коллекцию, а Consumer - как OUT-коллекцию.

Код: java
1.
2.
3.
4.
5.
	void copy(Supplier<? extends Bar> src, // IN-param, extends 
				Consumer<? super Bar> dest) { // OUT-param, super
		
		dest.accept(src.get()); 
	}



Всё как в уроке :)
...
Рейтинг: 0 / 0
Помогите с наследованием в генериках
    #38999489
Фотография Usman
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
chabapokНе подходит.
Код: java
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
public class Functional {
    
    @FunctionalInterface
    public static interface IFoo<T extends Number, R> extends java.util.function.Function<T, R> {
        
        R apply(T t);
    }
    
    public static class Foo<T extends Number> implements IFoo<T, String> {

        @Override
        public String apply(Number t) {
            return fooStaticMethod(t);
        }
        
        public String fooNonStaticMethod(T t) {
            return this.apply(t);
        }
        
        public static <T extends Number> String fooStaticMethod(T t) {
            return t.toString();
        }
    }
    
    public static class FooChild1<T extends Number> extends Foo<T> {
    }
    
    public static class FooChild2<T extends Number> extends Foo<T> {
        
        public static <T extends Number> String fooChildStaticMethod(T t) {
            return t.toString();
        }
    }
    
    public static <T extends Number> void func(IFoo<T, String> i, T t) {
        System.out.println(i.apply(t));
    }
    
    public static void main(String[] args) {
        Byte b = Byte.valueOf((byte)5);
        func(new Foo<Byte>()::fooNonStaticMethod, b);
        func(Foo::fooStaticMethod, b);
        
        Integer i = Integer.valueOf(10);
        func(new FooChild1<Integer>()::fooNonStaticMethod, i);
        func(FooChild1::fooStaticMethod, i);

        Double d = Double.valueOf(15);
        func(FooChild2::fooStaticMethod, d);
        func(new FooChild2<Double>()::fooNonStaticMethod, d);
        func(FooChild2::fooChildStaticMethod, d);
    }
}
...
Рейтинг: 0 / 0
Помогите с наследованием в генериках
    #39000443
chabapok
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Usman,

Во! Спасибо! Это оно!
Походу, отвечая на вопрос из первого поста, обьявлять f() надо так:
Код: java
1.
2.
public static <T extends Foo> void f( BiFunction<T, String, Integer> v ){
}
...
Рейтинг: 0 / 0
Помогите с наследованием в генериках
    #39000570
Фотография Диез
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
chabapokUsman,

Во! Спасибо! Это оно!
Походу, отвечая на вопрос из первого поста, обьявлять f() надо так:
Код: java
1.
2.
public static <T extends Foo> void f( BiFunction<T, String, Integer> v ){
}



Круто. А что теперь с этим делать? :)

Код: java
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
public class Proba {

	public static <T extends Foo> void f(BiFunction<T, String, Integer> v) {
		Integer result = v.apply(null, "123"); 
		System.out.println(result);
	}

	public static void main(String[] args) {
		f(FooChild2::func);
		f(FooChild1::func);
		f(Foo::func);
	}
}



Кроме null в вызове v.apply ничего не подставишь - ошибка компиляции.

Т.е. смысл у данного решения нулевой, особенно в свете "Reference to an instance method of an arbitrary object of a particular type"
...
Рейтинг: 0 / 0
Помогите с наследованием в генериках
    #39000679
Фотография Диез
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Всё, беру свои слова назад :))

Код: java
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
	public static <T extends Foo> void f(BiFunction<T, String, Integer> v, T t) {

		Integer result = v.apply(t, "");

		System.out.println(result);
	}

	public static void main(String[] args) {

		Foo foo = new Foo();
		FooChild1 f1 = new FooChild1();
		FooChild2 f2 = new FooChild2();

		f(Foo::func, foo);

		f(FooChild2::func, f2);

		f(FooChild1::func, f1);

	}
...
Рейтинг: 0 / 0
12 сообщений из 12, страница 1 из 1
Форумы / Java [игнор отключен] [закрыт для гостей] / Помогите с наследованием в генериках
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]