Groovy:从方法中动态确定预期的返回类型?

时间:2013-06-04 06:05:50

标签: groovy overloading return-type

Groovy中是否有可能在方法中确定预期的结果是什么?

实际上,这意味着按返回类型重载。

动机:数据库查询方法可能会也可能不会返回1或0 .. *结果。 如果是1,则会抛出;如果为0 .. *,它只会返回一个集合。

所以我想只有一个query(...)在这些情况下会返回List<Foo>Foo

List<Foo> foos = query("FROM Foo");
Foo foo        = query("FROM Foo f WHERE f.id = 1");

query伪代码将是:

Object query( String q ){
    if( Collection.class.isAssignableFrom( GET_CURRENT_METHODS_RETURN_TYPE ) ){
         return new LinkedList(){ ... }
    }
    if( Foo.class == GET_CURRENT_METHODS_RETURN_TYPE ) ){
         return new Foo(); // TODO
    }
}

奖金问题:某些语言是否支持此内容?

3 个答案:

答案 0 :(得分:2)

我不确定这究竟是您要找的,但看看是否有帮助:

class T {}

def func(t) {
    List <T> a = [new T(), new T()]
    T b = new T()

    if (t > 1) return (List <T>)a
    if (t == 1) return (T)b
}

assert func(1) instanceof T
assert func(2) instanceof List<T>

答案 1 :(得分:2)

方法返回重载意味着使用不同的返回类型编写相同的方法。 JVM允许它,尽管Java没有。 Groovy会这样做,但它无法正确解析将调用哪个方法。 There are some languages which support this,如C ++,Haskell,Perl和Ada。

至于你想要什么,是的,只需返回方法应返回的内容,并将def作为方法的返回类型。如果您不想要def返回类型,则需要List<Foo>Foo的超类型(当您使用CompileStatic/TypeChecked时,可以由编译器推断)。

对于调用者来说,旧的instanceof(或switch-case)可以解决问题:

class Foo{}

class Database {
  def query(String query) {
    if (query.contains(".id")) { // here you will make a call to database
      new Foo()
    } else {
      []
    }
  }
}

db = new Database()
assert db.query("from Foo f where f.id = 3") instanceof Foo
assert db.query("from Foo f") instanceof List

<强>更新

编译器会将常见的超类型推断为返回类型。如果它不是你想要使用的方法的东西,你将不得不根据它“分叉”。 an extension可以进行模式匹配,如果你不进入if s:

import groovy.transform.CompileStatic

@CompileStatic
class Cases {
  static main(args) {
    def returned = new Cases().query 10
    //returned[1] // doesn't work: returned is of the common type 
                  // OBJECT, which has no getAt method

    returned.case {
      when String then { println it } // will print "a string"
      when List then { List l -> println l.head() } // compiles fine, won't be executed
    }
  }

  def query(obj) {
    if (obj == 10) {
      "a string"
    } else {
      [1, 2, 3]
    }
  }
}

这里是Groovy推理的亮点:

import groovy.transform.CompileStatic

@CompileStatic
class Cases {
  static main(args) {
    assert new Cases().foo().baz == "w00t"
  }

  def foo() {
    new Foo(baz: "w00t")
  }
}

class Foo { String baz }

您编写def foo()并且知道该方法将返回Foo个对象。美丽。


如果在可能的实现中存在共同的超类,则将选择:

import groovy.transform.CompileStatic

@CompileStatic
class Cases {
  static main(args) {
    def bat = new Cases().animal 1
    assert bat.name == "bat"
    assert bat.favoriteBloodType == "A+" // Won't compile with error 
                                         // "No such property: favoriteBloodType
                                         // for class: Animal"
  }

  def animal(int animalCode) {
    if (animalCode == 1) {
      new Bat(name: "bat", favoriteBloodType: "A+")
    } else {
      new Chicken(name: "chicken", weight: 3.4)
    }
  }
}

abstract class Animal {
  String name
}

class Bat extends Animal {
  String favoriteBloodType
}

class Chicken extends Animal {
  BigDecimal weight
}

在您的情况下,编译器将推断FooList的共同超类型:Object

答案 2 :(得分:1)

Groovy允许您使用def关键字声明不带类型的变量。所以你可以写:

def foo = query("FROM Foo"); // "foo" will contain a List<Foo>

或:

def foo = query("FROM Foo f WHERE f.id = 1"); // "foo" will contain a Foo object

那就是说,由你决定让它以正确的方式运作。为了做到这一点,我建议你总是返回一个集合,它可能只包含一个项目。

根据经验,如果你期望不同的返回类型,你可能需要两种不同的行为来处理它们,所以有两种方法都没问题。