将类特定和特定于方法的泛型封装在一种类型中

时间:2014-09-29 09:34:03

标签: java generics

我们说我需要一个与通用Comparable类型相关的类:

class A<T extends Comparable<T>> {

    // this is just an example of usage of T type
    List<T> comparables;

    int compareSomething(T smth) {
        return comparables.get(0).compareTo(smth);
    }
}

该类具有签名中具有自己的泛型的方法:

<V> Future<V> submit(Callable<V> task) {
    return someExecutorService.submit(task);
}

现在,是否有可能将submit方法的输入限制为仅接受同时实施Callables的{​​{1}}?我第一次尝试这个:

T

但发现它不可能(出于here所述的原因)。有没有优雅的可能性绕过它?


编辑:我能想到的一个丑陋的可能性是将封装分成两种不同的类型并在<V, W extends T & Callable<V>> Future<V> submit(W task) { if(compareSomething(task) != 0) throw new RuntimeException(""); return someExecutorService.submit(task); } 中传递一个对象:

submit

主要缺点是方法签名变得更复杂,对于后一些代码,我还需要class A<T extends Comparable<T>> { // this is just an example of usage of T type List<T> comparables; int compareSomething(T smth) { return comparables.get(0).compareTo(smth); } <V> Future<V> submit(Callable<V> task, T comparable) { if(compareSomething(comparable) != 0) throw new RuntimeException(""); return someExecutorService.submit(task); } } s到T s的一对一映射。也许人们可以建议一种以适当的方式解决它的模式?...


编辑,采取两个:让我简要解释一下我想要实现的目标。我正在开发一个能够执行某种特殊任务调度的自定义线程池实现。为此,此服务仅接受一种特殊的Callable任务。那些Callable必须实现类似于Callable的自定义界面。通过使用此接口中的方法比较任务对,服务将:

  1. 如果传入任务被任何正在运行的任务阻止,则阻止该传入任务。
  2. 完成完成任务Comparable
  3. 后,调用待处理任务
  4. 通过比较暂定任务的执行顺序来确定。
  5. 阻塞/比较逻辑应由任务本身提供。这样,线程池类应该只定义池对象接受的Future特殊类型,并且它并不关心它们实际是什么类型的Comparable,什么是他们的回归类型。


    编辑,取三:基于Erick Robertson answer,现在可以阻止提交臭臭任务:

    Callable

    美好而轻松!希望有一天,这将有助于与我的情况相同的人。 : - )

7 个答案:

答案 0 :(得分:3)

使用Comparable<T>来约束参数,而不仅仅是T

如果唯一的标准是对象是CallableComparable,那么您可以将这两个接口放在参数上。如果需要填充另一个要求,甚至可以选择为参数添加命名类。您需要压制一个警告,但这是一种安全抑制,因为您知道T延伸Comparable<T>

public class A<T extends Comparable<T>> {

    // this is just an example of usage of T type
    List<T> comparables;

    ExecutorService someExecutorService = null;

    int compareSomething(T smth) {
        return this.comparables.get(0).compareTo(smth);
    }

    <V> Future<V> submit(Callable<V> task) {
        return this.someExecutorService.submit(task);
    }

    @SuppressWarnings("unchecked")
    <V, W extends Callable<V> & Comparable<T>> Future<V> betterSubmit(W task) {
        if(this.compareSomething((T) task) != 0)
            throw new RuntimeException("");
        return this.someExecutorService.submit(task);
    }
}

我遇到了另一个类的问题,我将它自己引用为自己的泛型参数。我不认为它是一个干净的实现,但我喜欢你的例子。它提供了一个很好的整洁用例,我可以在之后模拟一些新代码。我一般不喜欢压制警告,但我并不介意,因为只有这种方法会受到影响。我希望这会有所帮助!

答案 1 :(得分:2)

我认为Erick Robertson的answer最接近直接解决方案。我想建议一个不那么直接的(避免任何未经检查的演员表)。你写道:

  

此服务只接受一种特殊的Callable任务。那些Callable必须实现类似于Comparable的自定义界面。

如果我们实际声明你所描述的类型,我们会提出这个交叉点接口:

interface CustomCallable<V> extends Callable<V>, Comparable<CustomCallable<?>> { }

请注意它如何实现Comparable<CustomCallable<?>>,而不是Comparable<CustomCallable<V>>。这是因为我假设不同输出类型的CustomCallable应该仍然可以通过池相互比较。否则,将V作为方法类型参数是没有意义的。

假设您的池可以接受任何类型的交叉点接口,我们可以完全删除T并且语言限制没有实际意义:

class A {

    List<CustomCallable<?>> customCallables;

    int compareSomething(CustomCallable<?> smth) {
        return customCallables.get(0).compareTo(smth);
    }

    <V> Future<V> submit(CustomCallable<V> task) {
        if (compareSomething(task) != 0) {
            throw new RuntimeException("");
        }
        return someExecutorService.submit(task);
    }
}

如果您坚持在特定类型的自定义调用上参数化A,则会变得更加复杂。使其工作的唯一方法是在交叉点接口中添加“self type”参数:

interface CustomCallable<V, SELF> extends Callable<V>, Comparable<CustomCallable<?, ?>> { }

这里,SELF旨在表示实现者本身的类型。但请注意,无法执行此操作。有关自我类型及其警告的更多信息,请参阅我的答案:Is there a way to refer to the current type with a type variable?

添加自我类型后,A现在可以声明Tsubmit然后坚持使用它提供的自定义调用的自我类型:

class A<T extends CustomCallable<?, ?>> {

    List<CustomCallable<?, T>> customCallables;

    int compareSomething(CustomCallable<?, T> smth) {
        return customCallables.get(0).compareTo(smth);
    }

    <V> Future<V> submit(CustomCallable<V, T> task) {
        if (compareSomething(task) != 0) {
            throw new RuntimeException("");
        }
        return someExecutorService.submit(task);
    }
}

以下是CustomCallable实现的示例,它解析了自我类型:

class MyCustomCallable<V> implements CustomCallable<V, MyCustomCallable<?>> {
    ...
}

与之前类似,请注意自我类型如何“放松”到MyCustomCallable<?>而不是MyCustomCallable<V>,因为不同的输出类型可以通过设计互换。

这是一个用法示例:

A<MyCustomCallable<?>> pool = new A<>();

MyCustomCallable<String> strCallable = ...;
MyCustomCallable<Integer> intCallable = ...;

Future<String> strFuture = pool.submit(strCallable);
Future<Integer> intFuture = pool.submit(intCallable);

答案 2 :(得分:1)

我不知道如何将T方法参数中的泛型类参数VCallable<V>结合起来,原因在于答案中列出的原因联系的帖子。但是,也许这是一个选项 - 我无法估计会有多么违反直觉的情况 - 改变将任务提交给在任务本身上调用的方法的行为?有些东西......

class A<T extends Comparable<T> & Callable<?>> {

    public static abstract class Submittable<T extends Submittable<T,V>,V>
      implements Comparable<T>, Callable<V> {
        // ugly, but unavoidable
        @SuppressWarnings("unchecked")
        public Future<V> submitTo(A<? super T> scheduler) {
          return (Future<V>) scheduler.submit((T) this);
        }
    }

    // this is just an example of usage of T type
    List<T> comparables;

    int compareSomething(T smth) {
        return comparables.get(0).compareTo(smth);
    }

    // *can* be left public, but that way you enforce a single way
    // of submitting tasks, namely via submitTo
    private Future<?> submit(T task) {
        if(compareSomething(task) != 0)
            throw new RuntimeException("");
        // the following cast is a save conversion - you could also write it cast-free
        // as Callable<?> callable = task; ...submit(callable);
        return someExecutorService.submit((Callable<?>) task);
    }
}

现在,您的任务都必须继承A.Submittable,然后可以通过submitTo提交给调度程序实例,并为正确的类型Future<V>返回V

当然,这至关重要依赖于T实际上是Submittable的子类的自我类型。 submitTo中发生了什么 - 好吧,丑陋。但它确实为您提供了返回Future<V>正确类型的便利。

答案 3 :(得分:0)

这是否解决了这个问题?

class A<V, T extends MyT<T, V>> {

    // this is just an example of usage of T type
    List<T> comparables;

    int compareSomething(T smth) {
        return comparables.get(0).compareTo(smth);
    }

    public <V> Future<V> submit(MyT<T, V> task) {
        return ...;
    }
}

class MyT<T, V> implements Comparable<T>, Callable<V> {

    @Override
    public int compareTo(T o) {
        return 0;
    }

    @Override
    public V call()
        throws Exception {

        return ...;
    }
}

答案 4 :(得分:0)

这样的事情可行:

class A<T extends Comparable<T>> {
    List<T> comparables;

    int compareSomething(T smth) {
        return comparables.get(0).compareTo(smth);
    }

    public Future<T> submit(B<T> task) {
        return someExecutorService.submit(task);
    }

    public class B<T> implements Callable<T> {
        ...
    }  
}

答案 5 :(得分:0)

好的,真的最后一次尝试。 你可以将泛型函数更改为泛型嵌套或内部类吗?像这样:

class A<T extends Comparable<T> >  {


    // this is just an example of usage of T type
    List<T> comparables;

    int compareSomething(T smth) {
        return comparables.get(0).compareTo(smth);
    }

    class B<V> 
     {
      T t;
      T getT() { return t; }
      Callable <V> v;
      Callable <V> getV() { return v; }
      public Future<V> submit(B<V> task) {
           if(compareSomething(task.getT()) != 0)
            throw new RuntimeException("");
        return SomeExecutorService.submit(task.getV());
    }
}

答案 6 :(得分:0)

为什么不会像这样简单的工作?

public class CustomThreadPool {
    public <CallableReturn> Future<CallableReturn> submit(SelfSchedulingCallable<CallableReturn> task) {
        // Use comparePriority or any other interface methods needed to determine scheduling
    }
}

public interface SelfSchedulingCallable<V> extends Callable<V> {
    public boolean isBlockedBy(SelfSchedulingCallable<?> otherTask); // Doesn't care about the return type of otherTask
    public boolean invokeAfter(SelfSchedulingCallable<?> otherTask);
    public int comparePriority(SelfSchedulingCallable<?> otherTask);
}