我们说我需要一个与通用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
的自定义界面。通过使用此接口中的方法比较任务对,服务将:
Comparable
。阻塞/比较逻辑应由任务本身提供。这样,线程池类应该只定义池对象接受的Future
特殊类型,并且它并不关心它们实际是什么类型的Comparable
,什么是他们的回归类型。
编辑,取三:基于Erick Robertson answer,现在可以阻止提交臭臭任务:
Callable
美好而轻松!希望有一天,这将有助于与我的情况相同的人。 : - )
答案 0 :(得分:3)
使用Comparable<T>
来约束参数,而不仅仅是T
。
如果唯一的标准是对象是Callable
和Comparable
,那么您可以将这两个接口放在参数上。如果需要填充另一个要求,甚至可以选择为参数添加命名类。您需要压制一个警告,但这是一种安全抑制,因为您知道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
现在可以声明T
,submit
然后坚持使用它提供的自定义调用的自我类型:
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
方法参数中的泛型类参数V
和Callable<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);
}