Java泛型可以用值而不是类型参数化吗?
假设我想定义结构相似的类型,但不同的参数可能是整数或其他东西。
是否可以在 Java 中定义由整数甚至任意对象参数化的一系列类?
考虑以下伪代码(无法编译):
/**
* String of a certain length n and a method to reduce to length n-1
*/
public class StringN<int n> {
private String str;
public StringN( String str) {
if(str.length() != n) {
throw new IllegalArgumentException("string is not of required length!");
}
this.str = str;
}
public StringN<n-1> reduce() {
return new StringN<n-1>(s.substring(0, s.length() - 1));
}
@Override
public String toString() {
return str;
}
}
我想到的其他更自然的例子是数学中的张量积,所以如果想要将空间 R^n 定义为 Java 类或在函数式编程中定义“arity”,那么将参数“n”放在哪里-Function<>空间。那么如何定义一组具有不同元数的类,由 n 参数化?
如果这在 Java 中是不可能的,那么这个概念是否存在于其他更多功能的语言中?它的正确名称是什么?(比如“参数化类”?)
编辑:作为对评论的反应,最后一部分只是知道这样一个概念的总称,而不是绕道其他语言。
回答
您的问题很有趣,但我认为您假设满足您需求的解决方案必然是参数化类,这走得太远了。
参数化类是数据类型的组合,而不是值。
由于您不需要 compile 对您的代码强制执行任何额外的静态类型检查,我认为一个程序化的解决方案就足够了:
- 第一步:将伪参数“int n”移动到最终变量:
public class StringN {
private final int n;
private String str;
public StringN( String str) {
if(str.length() != n) {
throw new IllegalArgumentException("string is not of required length!");
}
this.str = str;
}
public StringN reduce() {
return new StringN(s.substring(0, s.length() - 1));
}
@Override
public String toString() {
return str;
}
}
-
当然,这还没有编译。您必须
n在每个构造函数(声明和调用)上初始化变量。 -
如果您对将参数
n作为公共构造函数调用的一部分公开这一事实感到不舒服,可以通过将构造函数限制为包访问并将构造责任带到新的Factory类来解决,这必须是创建的唯一公共方法StringN 对象。
public StringNFactory
{
private final int n;
public StringNFactory(int n)
{
this.n=n;
}
public StringN create(String s)
{
return new StringN(this.n, s);
}
}
- 在这里,从不分配 `n`,这不会编译。似乎 `n` 必须作为构造函数参数传递。
- 是的,我的朋友们,第一段代码不会编译:我在第 2 点已经清楚地说明了。我只是想开发我的方法 **逐步** 清楚我在每一步做什么.
回答
唉,Java 要求类型参数是类型(实际上,它甚至要求它们是引用类型),并且由于所有整数都是相同的类型,因此编译器无法根据整数的值来区分泛型。
通常的解决方法是为每个可能(或需要)的值声明一个单独的类型。要共享结构,您可以使用抽象基类。如果基类需要任何具体类型,子类可以将它们作为类型参数传递:
abstract class StringN<S extends StringN<S,P>, P extends StringN<P,?>>
implements Comparable<S> {
final String value;
protected StringN(String value, int n) {
if (value.length() != n) {
throw new IllegalArgumentException(value);
}
this.value = value;
}
@Override
public int compareTo(S o) {
return value.compareTo(o.value);
}
abstract P newP(String value);
public P removeLast() {
return newP(value.substring(0, value.length() - 1));
}
}
class String0 extends StringN<String0, String0> {
protected String0(String value) {
super(value, 0);
}
@Override
String0 newP(String value) {
throw new UnsupportedOperationException();
}
}
class String1 extends StringN<String1, String0> {
protected String1(String value) {
super(value, 1);
}
@Override
String0 newP(String value) {
return new String0(value);
}
}
class String2 extends StringN<String2, String1> {
protected String2(String value) {
super(value, 2);
}
@Override
String1 newP(String value) {
return new String1(value);
}
}
public class Test {
public static void main(String[] args) {
String2 s2 = new String2("hi");
String1 s1 = s2.removeLast();
s1.compareTo(s2); // compilation error: The method compareTo(String1) is not applicable for the arguments (String2)
}
}
如您所见,只要值集是有限的并且预先知道,您甚至可以教编译器进行计数:-)
但是,它变得相当笨拙且难以理解,这就是为什么很少使用此类解决方法的原因。