简体   繁体   English

Java通用通配符用法

[英]Java generic wildcard usages

This is related to java generic wild card. 这与java通用通配符有关。

I have interface like this. 我有这样的界面。

public interface Processer<P, X> {
    void process(P parent, X result);
}

An implementation like this. 这样的实现。

public class FirstProcesser implements Processer<User, String> {
    @Override
    public void process(User parent, String result) {

    }
}

And I'm using processer as this. 我正在使用处理器。

public class Executor {
    private Processer<?, String> processer;

    private  int i;
    public void setProcesser(Processer<?, String> processer) {
        this.processer = processer;
    }

    private String generateString() {
        return "String " + i++;
    }

    public <P> void execute(P parent) {
        processer.process(parent, generateString());
    }

    public static void main(String[] args) {
        Executor executor = new Executor();
        executor.setProcesser(new FirstProcesser());
        User user = new User();
        executor.execute(user);
    }
}

But here 但在这儿

public <P> void execute(P parent) {
    processer.process(parent, generateString());
}

it gives compile error Error:(18, 27) java: incompatible types: P cannot be converted to capture#1 of ? 它给出了编译错误错误:(18,27)java:不兼容的类型:P无法转换为捕获的#1?

I need to understand why this give an error. 我需要了解为什么这会导致错误。 also solution. 也解决。

The wildcard basically means "I don't care which type is used here". 通配符基本上表示“我不在乎此处使用哪种类型”。 In your case, you definitely do care though: the first type parameter of your processor must be the same as the P type in the execute method. 对于您的情况,您一定要小心:处理器的第一个type参数必须与execute方法中的P type相同。

With the current code, you could call execute(1) , which would try to call the FirstProcesser with an integer as argument, which obviously makes no sense, hence why the compiler forbids it. 使用当前代码,您可以调用execute(1) ,该方法将尝试使用整数作为参数来调用FirstProcesser,这显然没有意义,因此为什么编译器禁止使用它。

The easiest solution would be to make your Executor class generic, instead of only the execute method: 最简单的解决方案是使您的Executor类具有泛型,而不仅仅是execute方法:

public class Executor<P> {
    private Processer<P, String> processer;

    private int i;
    public void setProcesser(Processer<P, String> processer) {
        this.processer = processer;
    }

    private String generateString() {
        return "String " + i++;
    }

    public void execute(P parent) {
        processer.process(parent, generateString());
    }

    public static void main(String[] args) {
        Executor executor = new Executor<User>();
        executor.setProcesser(new FirstProcesser());
        User user = new User();
        executor.execute(user);
    }
}

Because processor can have first type argument of anything. 因为processor可以具有任何东西的第一类型参数。 You may have assigned a Process<Foo, String> to it, and of course compiler will complain as it can be something different from P in your execute() . 您可能已经为其分配了一个Process<Foo, String> ,当然编译器会抱怨,因为它可能与execute() P有所不同。

You may want to make your Executor a generic class: 您可能希望将Executor设为通用类:

class Executor<T> {
    private Processer<T, String> processer;

    public void setProcesser(Processer<T, String> processer) {
        this.processer = processer;
    }

    public void execute(T parent) {
        processer.process(parent, generateString());
    }
}

and your main will look like: 您的main将如下所示:

    Executor<User> executor = new Executor<User>();
    executor.setProcesser(new FirstProcesser());
    User user = new User();
    executor.execute(user);

In response to comments: 回应评论:

There is no proper solution with proper use of Generics here, because what you are doing is contradicting: On one hand you say you do not care about first type argument of Processor (hence private Processor<?, String> processor ), but on the other hand you DO really care about it (your execute ). 没有正确使用泛型这里没有妥善的解决办法,因为你在做什么是矛盾的:一方面,你说你不关心的第一个类型参数Processor (因此private Processor<?, String> processor ),但在另一方面,您确实很在乎它(您的execute )。 Compiler is simply doing its work right as it is absolutely legal for you to assign a Processor<Foo,String> to it. 编译器只是在正确地执行其工作,因为为您分配Processor<Foo,String>绝对合法。

If you don't really care about generics and is willing to suffer from poor design, then don't use generics. 如果您不太在意泛型并且愿意接受糟糕的设计,那么请不要使用泛型。

Just keep Processor a raw type in Executor and suppress all unchecked warning: 只需在Executor中将Processor保持为原始类型,并禁止所有未检查的警告:

ie

class Executor {
    private Processor processor;

    @SuppressWarnings("unchecked")
    public void setProcessor(Processor<?, String> processor) {
        this.processor = processor;
    }

    // your generic method does not do any meaningful check.
    // just pass an Object to it
    @SuppressWarnings("unchecked")
    public void execute(Object parent) {
        processor.process(parent, "");
    }
}

And if it is me, I will go one step further: 如果是我,我将进一步前进:

Provide an Executor that is properly designed (eg calling it TypedExecutor ). 提供经过适当设计的执行器(例如,将其TypedExecutor )。 All new code should use the new, properly designed TypedExecutor . 所有新代码都应使用经过适当设计的新TypedExecutor Original Executor is kept for sake of backward compatibility, and delegate its work to TypedExecutor . 保留原始Executor是为了向后兼容,并将其工作委托给TypedExecutor

Hence look like: 因此看起来像:

class TypedExecutor<T> {
    private Processor<T, String> processor;

    public void setProcessor(Processor<T, String> processor) {
        this.processor = processor;
    }

    public void execute(T parent) {
        processor.process(parent, "");
    }
}

@SuppressWarnings("unchecked")
class Executor {
    private TypedExecutor executor = new TypedExecutor();

    public void setProcessor(Processor<?, String> processor) {
        this.executor.setProcessor(processor);
    }

    public void execute(Object parent) {
        this.executor.execute(parent);
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM