2014-09-29 97 views
13

比方说,我需要为界,通用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也实现T的可能性?我第一次尝试这样的:

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

,但发现这是不可能的(原因描述here)。有没有优雅的可能性绕过它?


编辑:一个丑陋可能性我能想到的是在两种不同类型的分裂封装,并通过在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); 
    } 
} 

的主要缺点是,该方法签名变得比较复杂,我也需要一段后者代码有些人Callable■一个一对一的映射T秒。 ?也许我们可以认为,解决它以适当的方式..


编辑模式,采取两种:让我来简单解释一下我想要实现的。我正在开发一个能够执行某种特殊任务调度的自定义线程池实现。为此,该服务只接受一种特殊的Callable任务。这些Callable■找实现自定义界面,类似于Comparable之一。通过比较使用此界面中的方法的任务对,该服务将:

  1. 如果传入任务被任何正在运行的任务阻塞,则阻止该传入任务。
  2. 在完成任务的Future完成调用尚未完成的任务。
  3. 通过比较他们确定的待处理任务的执行顺序。

阻挡/比较逻辑应该由任务本身来提供。这样,线程池类应该只定义池对象正在接受什么样的特殊类型,它根本不关心它们是什么类型的,它们的返回类型是什么。


编辑,采取三种:基于Erick Robertsonanswer,它现在能够防止发臭任务提交:

public static void test(String[] args) { 
    A<Valid> scheduler = new A<>(); 
    scheduler.betterSubmit(new Valid()); // applies to method signature 
    scheduler.betterSubmit(new Forbidden()); // rejected on compile time 
    scheduler.betterSubmit(new ConformWithValid()); // still appliable because all required interfaces implementations recognised 
} 

// just a bunch of test classes 

private static class Valid implements Comparable<Valid>, Callable<Void> { 

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

    @Override 
    public Void call() throws Exception { 
     return null; 
    } 
} 

private static class Forbidden implements Comparable<Forbidden>, Callable<Void> { 

    @Override 
    public int compareTo(Forbidden o) { 
     return -1; 
    } 

    @Override 
    public Void call() throws Exception { 
     return null; 
    } 
} 

private static class ConformWithValid implements Comparable<Valid>, Callable<Boolean> { 

    @Override 
    public int compareTo(Valid o) { 
     return 1; 
    } 

    @Override 
    public Boolean call() throws Exception { 
     return Boolean.FALSE; 
    } 
} 

尼斯和容易!希望有一天这能帮助和我一样的人。 :-)

+4

密切相关:?在多重限制的类型参数为什么我不能用一个类型参数(http://stackoverflow.com/q/197190 ) – 2014-09-29 09:44:43

回答

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

我碰到的另一个类,我有它本身作为参考这样自己的通用参数同样的问题。我不认为这是一个干净的实施,但我喜欢你的榜样。它提供了一个很好的整洁用例,我可能会在后面设计一些新的代码。我通常不喜欢压制警告,但我并不介意,因为只有这种方法受到影响。我希望这个能帮上忙!

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 ...; 
    } 
} 
+0

不幸的是,因为MyT 不符合'T','compareSomething'不适用于'MyT'。 (你也可以想象将提交的任务添加到“comparables”列表或任何类似的东西,而不是调用'compareSomething',那么确切的功能在这一点上并不重要。)另外 - 虽然这不会影响实际问题 - t想要将整个类绑定到特定的返回类型'Callable's,所以我不能在类签名中允许'V'。 – hoefling 2014-09-30 15:57:44

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> { 
     ... 
    } 
} 
+0

你错过了点 - 请再次看看我的'submit'方法... – hoefling 2014-10-01 16:37:16

0

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

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

同样的问题在这里 - 定义'V'作为'Callable's结果类型将绑定整个类到一个特定类型' Callable's。此外,定义'V extends Callable '根本没有意义 - 这个类只能继续'Callable's,返回另一个'Callable's。我会用更多的细节来提高这个问题,以清除那里的一些雾。 – hoefling 2014-10-02 21:52:49

+0

好的,gotcha。那么结合这样的方法怎么样?我猜不是--V在单个T内仍然不能自由变化。 – 2014-10-02 22:10:10

+0

仍然存在类受限于“V”的问题。请阅读我对问题所做的第二次编辑和解释;希望它能让整件事更容易理解。尽管如此,我开始认为通过在全班范围内改变泛型的使用方式,没有办法解决实际问题;我可能需要的是类设计的一个好主意。 – hoefling 2014-10-02 22:17:46

1

我不明白你怎么可以结合两者的通用类参数T并在Callable<V>方法参数的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发生了什么 - 很好,很丑陋。但它确实为您提供了返回正确类型的便利。

+0

谢谢,这可能也是一种选择,虽然我接受的答案在整个班级中提供的表达较少。 – hoefling 2014-10-07 12:11:55

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); 
} 
2

我认为埃里克·罗伯逊的answer最接近的直接解决方案。我想提出一个不太直接的建议(避免任何未经检查的强制转换)。您写道:

此服务只接受一种特殊的Callable任务。这些Callable■找实现自定义界面,类似于Comparable之一。

如果我们实际上宣告你所描述的类型,我们想出了这个路口​​接口:

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

注意它是如何实现Comparable<CustomCallable<?>>,不Comparable<CustomCallable<V>>。这是因为我假设CustomCallable s的不同输出类型仍应该可以在池中相互比较。否则,将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,它变得更加复杂。使它工作的唯一方法是一种“自我型”参数添加到路口接口:

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); 
+0

这也是一个非常好的答案,我喜欢你的想法和干净的解释。不幸的是,我必须将调度程序类绑定到特定的任务类型。尽管如此,你只是给了我一个答案,我现在仍然坚持着另一个问题,所以这是一个耻辱,我不能给你一个赏金...... – hoefling 2014-10-07 12:18:34