强制执行Java方法返回类型以适应某些通用签名 [英] Enforcing Java method return type to fit certain Generic signature

查看:96
本文介绍了强制执行Java方法返回类型以适应某些通用签名的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我有这个接口:

  interface Foo< T extends Foo< T>> {// ...} 

以下三个类实现它:

  class TrueFoo实现了Foo< TrueFoo> {// ...} 

类FalseFoo实现Foo< FalseFoo> {// ...}

class WrongFoo实现Foo< FalseFoo> {// ...}
// ^^^^^^^^这里错了,只有
// Foo< WrongFoo>应该被允许

以下方法的方法签名需要执行以使我可以返回TrueFoo或FalseFoo类型的对象,但不包含WrongFoo ...

  public ???? getFoo(boolean which){
返回哪个? new TrueFoo():new FalseFoo();
}

以下行不通:




  • 这也允许我返回一个新的WrongFoo():

      public Foo<?> getFoo(boolean which){
    返回哪个? new TrueFoo():new FalseFoo();
    }


  • 这不允许我返回任何东西以下解释):

      public< FooType extends Foo< FooType>> FooType getFoo(boolean which){
    返回哪个? new TrueFoo():new FalseFoo();





    $ b

    问题在于FooType的类型是由I调用函数 getFoo(),而不是通过实际尝试返回的对象。




调用时,实施此签名很简单,顺便说一句:

  public< FooType extends Foo< FooType>> void test(FooType foo){
//用foo
做些事情}

如果你试图用一个WrongFoo的实例调用它,这会给你一个绑定的不匹配错误。



让我觉得应该有一种方法来为方法返回



或者是否有办法在Foo的接口定义中强制执行此签名?



编辑:



为了帮助可视化这个界面应该做什么,你可以考虑这个版本:

  interface CanCopy< Type extends CanCopy< Type>> {
public Type copy();
}



显然,像 Foo这样的类实现了CanCopy< Bar> 没有任何意义,只有 Foo实现了CanCopy< Foo> Bar实现了CanCopy< Bar>



编辑2:

- 解释存在的解决方案,我可以自己定义下面的助手类:

$ p $ class FooResult {
private final Foo< ?> FOO;

public< FooType extends Foo< FooType>> FooResult(FooType foo){
this.foo = foo;
}

@SuppressWarnings(unchecked)
public< FooType extends Foo< FooType>> FooType getFoo(){
return(FooType)foo;
}
}

然后我可以要求我的getFoo方法是这个键入:

  public FooResult getFoo(boolean which){
return which?新的FooResult(new TrueFoo()):new FooResult(new WrongFoo());
}

这样,我就无法从我的get方法中返回一个WrongFoo 。

但是,显然,这有点过于复杂,不如Java泛型通常倾向于编写代码(以我的经验)...因此,可以不知何故被缩短了?



编辑3:



  interface Foo< FooType extends Foo< FooType>> {
//其他接口定义

/ **
*请使用以下行来实现:< br />< br />
*& nbsp;& nbsp;& nbsp;< code> return this;< / code>
* @return< code>此< / code>
* /
public FooType returnThis();

$ / code>

现在,如果有人试图按上述方式实现WrongFoo类,他会必须提供方法 FalseFoo returnThis(),并且如果他按照doctype的要求简单地将它实现为返回这个这条线会抛出一个错误。

这不是一个保证,但它是一个相当不错的模拟检查,以防止不小心复制类的错误。这是一个很好的解决方案。



还有什么想法?

解决方案

您使用泛型时遇到的问题是症状,而不是问题:您要求界面变得依赖于具体的实现。如果你想让你的getFoo接口方法返回一个不是WrongFoo的东西,你必须返回一个是TrueFoo或者FalseFoo而不是WrongFoo的东西。你的类层次结构没有这种特殊的间接性。



要添加它,您需要执行以下操作:

  class TrueFoo extends SpecialFoo {...} 
$ b $ class FalseFoo extends SpecialFoo {...}
$ b $ class WrongFoo实现Foo< WrongFoo> {...}

类SpecialFoo实现Foo< SpecialFoo> {
SpecialFoo getFoo(){...}
}

Java支持协变返回类型,所以通过返回SpecialFoo,我们可以实现在Foo接口中创建的协定: Foo getFoo(){...}



如果您需要特定于TrueFoo和FalseFoo的行为,则可以进一步对SpecialFoo进行参数化,等等,

编辑:



在阅读您的编辑之后,我不相信您要完成的任务是否受该语言支持。看起来你想限制基于具体实现的接口,根据定义,它不是一个接口。我认为以下示例与您要查找的内容非常接近:

  interface CanCopy< T延伸了CanCopy< T>> {
T copy();
}

interface FooCanCopy扩展了CanCopy< Foo> {
}

界面BarCanCopy扩展了CanCopy< Bar> {
}

class Foo实现FooCanCopy {

public Foo copy(){
return null;



class Bar实现BarCanCopy {

public bar copy(){
return null;


$ / code $ / pre

我希望现在清楚为什么这种方法不适用工作,即使在这里,你仍然无法阻止某人做类似于的类Baz实现FooCanCopy 。我更想知道你为什么要这么做?如果为了保护开发人员不犯错误,可能会有其他选择,例如反思单元测试或打包更改。

Let's say I have this interface:

interface Foo<T extends Foo<T>> { // ... }

And the following three classes implementing it:

class TrueFoo  implements Foo<TrueFoo > { // ... }

class FalseFoo implements Foo<FalseFoo> { // ... }

class WrongFoo implements Foo<FalseFoo> { // ... }
//                            ^^^^^^^^ this here is wrong, only 
//                                     Foo<WrongFoo> should be allowed

What would the method signature of the following method need to be to enforce that I can return objects of type TrueFoo or FalseFoo, but not WrongFoo...

public ???? getFoo(boolean which) {
    return which ? new TrueFoo() : new FalseFoo();
}

The following don't work:

  • This allows me to also return a new WrongFoo():

    public Foo<?> getFoo(boolean which) {
        return which ? new TrueFoo() : new FalseFoo();
    }
    

  • This doesn't allow me to return anything (explanation following):

    public <FooType extends Foo<FooType>> FooType getFoo(boolean which) {
        return which ? new TrueFoo() : new FalseFoo();
    }
    

    The problem is that the type of FooType is determined by the context in which I call the function getFoo() and not by the object I am actually trying to return.

Enforcing this signature when calling a method is easy, by the way:

public <FooType extends Foo<FooType>> void test(FooType foo) {
    // Do something with foo
}

This will give you a bound mismatch error if you try to call it with an instance of WrongFoo.

Makes me think there should be a way to do this for method return types as well.

Or is there a way to enforce this signature already in the interface definition for Foo?

EDIT:

To help visualize what this interface is supposed to do, you can for example think of this version of it:

interface CanCopy<Type extends CanCopy<Type>> {
    public Type copy();
}

Clearly a class like Foo implements CanCopy<Bar> doesn't make any sense, only Foo implements CanCopy<Foo> or Bar implements CanCopy<Bar>.

EDIT 2:

Just as proof-of-concept that solutions exist, I could define myself the following helper class:

class FooResult {
    private final Foo<?> foo;

    public <FooType extends Foo<FooType>> FooResult(FooType foo) {
        this.foo = foo;
    }

    @SuppressWarnings("unchecked")
    public <FooType extends Foo<FooType>> FooType getFoo() {
        return (FooType) foo;
    }
}

Then I can require my getFoo method to be of this type:

public FooResult getFoo(boolean which) {
    return which ? new FooResult(new TrueFoo()) : new FooResult(new WrongFoo());
}

That way, I will not be able to return a WrongFoo from my get method.

But this, clearly, is a little too convoluted to be as elegant as Java Generics usually tend to make code (in my experience)... So, can this be shortened somehow?

EDIT 3:

I found another way to provide a check for someone implementing the interface by defining it like this:

interface Foo<FooType extends Foo<FooType>> { 
    // Other interface definitions

    /**
     * Please implement with just this line in it:<br/><br/>
     * &nbsp;&nbsp;&nbsp;&nbsp;<code>return this;</code>
     * @return <code>this</code>
     */
    public FooType returnThis();
}

Now, if someone tries to implement the WrongFoo class as described above, he will have to provide the method FalseFoo returnThis() and if he simply implements it as return this as requested in the doctype, that line will throw an error.

It's not a guarantee, but it's a pretty good dummy-check against mistakes that come from careless copying of a class... And in a case where a message with this signature is required anyways, it would be a great solution.

Any more ideas?

解决方案

The issue you're having with generics is the symptom, not the problem: You're asking your interface to become dependent upon your concrete implementation. If you want your getFoo interface method to return something that is not a WrongFoo, you must return something that is a TrueFoo or a FalseFoo but not a WrongFoo. Your class hierarchy does not have this particular indirection.

To add it, you would need to do something like the following:

class TrueFoo extends SpecialFoo {...}

class FalseFoo extends SpecialFoo {...}

class WrongFoo implements Foo<WrongFoo> {...}

class SpecialFoo implements Foo<SpecialFoo> {  
  SpecialFoo getFoo() {...}
}

Remember that Java supports covariant return types, so by returning SpecialFoo we can implement the contract created in the Foo interface: Foo getFoo() {...}

If you were to need behavior specific to TrueFoo and FalseFoo, you could further parameterize SpecialFoo, and so on, turtles all the way down.

EDIT:

After reading your edit, I don't believe what you're trying to accomplish is supported by the language. It seems like you want to constrain the interface based on the concrete implementation, which would by definition make it not an interface. I think the following example is as close as you're going to get to what you're looking for:

interface CanCopy<T extends CanCopy<T>> {
       T copy();
}

interface FooCanCopy extends CanCopy<Foo> {
}

interface BarCanCopy extends CanCopy<Bar> {
}

class Foo implements FooCanCopy {

    public Foo copy() {
        return null;
    }
}

class Bar implements BarCanCopy {

    public Bar copy() {
        return null;
    }
}

I hope its clear now why the approach doesn't work, even here you still can't prevent someone from doing something like class Baz implements FooCanCopy either. I'd be more curious to know why you're trying to do this? If its to protect developers from making a mistake, there might be other options, i.e. introspecting unit tests or packaging changes.

这篇关于强制执行Java方法返回类型以适应某些通用签名的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆