我有1个界面和3个类。我希望该类能够实现需要transform
方法的接口。此方法必须存在,但每个类不能超过一个。我不知道这个类所采用的参数数量。
示例:
public interface A{
public void transform(Object ... args);
}
public class B implements A{
public void transform(String a){
System.out.println(a);
}
}
public class C implements A{
public void transform(Integer a, Character b){
System.out.println(a+b);
}
}
// super generic case if possible with Objects + primitive
public class D implements A{
public void transform(int a, String b){
System.out.println(a+b);
}
}
这不起作用。但我希望你有这个主意。这样的东西在java中可能吗?我应该如何以通用方式调用它们?假设我有其他方法,如:
void callTransf(A a, Object ... objs){
Method m = a.getClass().getMethods()[0];
m.invoke(a, objs)
}
答案 0 :(得分:10)
一个切实可行的解决方案是将接口声明为通用接口:
public interface Transformation<S, R> {
R transform(S source);
}
类型参数S
扮演源角色;类型参数R
扮演结果角色。
您现在可以为每个不同的转换创建源类和结果类。一个例子:
public final class TransformationSourceForA {
// Here you declare whatever fields and methods you need for an A source.
// For example:
int a;
String b;
}
public final class TransformationResultForA {
// Here you declare whatever fields and methods you need for an A result.
}
然后将变换声明如下:
public final class TransformationA implements Transformation<TransformationSourceForA, TransformationResultForA> {
@Override
public TransformationResultForA transform(TransformationSourceForA source) { ... }
}
原则是将不同字段的需求委托给一个类而不是方法的参数。
答案 1 :(得分:3)
你可以通过一些改变和功能编程的帮助来实现你想要的......
主要思想是transform
方法没有收到任何参数。相反,它将返回一些功能接口的实例。
此功能接口的实现将包含transform
方法在有参数时执行的代码。
为了表示A
接口的每个子类的不同类型和/或不同数量的参数的参数,我们将在方法transform
的返回类型中使用协方差。< / p>
这意味着功能接口将是通用的(因此A
的每个子类的参数类型可以不同),并且将存在将扩展此功能接口的子接口,每个接口都接受单个抽象方法中的参数数量不同。这将允许transform()
方法的返回值包含1,2,3,...等参数。
要执行transform()
方法返回的代码,我们会这样做:
instanceOfB.transform().execute("hello");
instanceOfC.transform().execute(1, 'a');
instanceOfD.transform().execute(1, "hello");
最后,为了能够以通用方式执行代码,基本功能接口定义了一个varargs方法executeVariadic(Object... args)
,它将被每个子功能接口实现为默认方法,委托给它execute
方法并根据需要转换参数。
让我们首先将您的A
界面重命名为更具描述性的界面。在定义名为transform
的方法时,请将其命名为Transformer
。
然后,让我们创建一个功能接口,代表transform
接口的Transformer
方法。这是:
@FunctionalInterface
public interface Transformation {
void executeVariadic(Object... args);
}
此接口只定义了一个接收Object...
varargs参数的抽象方法(SAM)。它存在,以便子接口可以覆盖它。
现在,让我们创建一个扩展Transformation1
界面的Transformation
功能界面:
@FunctionalInterface
public interface Transformation1<A> extends Transformation {
void execute(A a);
@Override
@SuppressWarnings("unchecked")
default void executeVariadic(Object... args) {
this.execute((A) args[0]);
}
}
此Transformation1<A>
功能接口是通用的,它定义了单个抽象方法execute
,它接受一个A
类型的参数。 executeVariadic
方法被覆盖为默认方法,将其执行委托给execute
方法,相应地转换第一个参数。这个演员会产生一个警告,但是哦,好吧......我们最好学会忍受它。
现在,让我们创建一个带有两个泛型类型参数的模拟接口和一个execute
方法,该方法接收两个类型与泛型类型参数匹配的参数:
@FunctionalInterface
public interface Transformation2<A, B> extends Transformation {
void execute(A a, B b);
@Override
@SuppressWarnings("unchecked")
default void executeVariadic(Object... args) {
this.execute((A) args[0], (B) args[1]);
}
}
这个想法是一样的:Transformation2
接口扩展了Transformation
接口,我们覆盖executeVariadic
方法,以便将它委托给execute
方法,相应的参数(并抑制恼人的警告)。
为了完整起见,我们引入Transformation3
接口,类似于之前的TransformationX
接口:
@FunctionalInterface
public interface Transformation3<A, B, C> extends Transformation {
void execute(A a, B b, C c);
@Override
@SuppressWarnings("unchecked")
default void executeVariadic(Object... args) {
this.execute((A) args[0], (B) args[1], (C) args[2]);
}
}
希望现在的模式很清楚。你应该创建尽可能多的TransformationX
接口作为你想要支持transform
接口Transformer
接口的A
方法的参数,记住我的问题改名为。)
到目前为止,我知道这个答案很长,但我需要定义上面的接口,以便现在可以将它们放在一起。
还记得你的A
界面吗?我们不仅要将其名称更改为Transformer
,还要将其transform
方法的签名更改为:
@FunctionalInterface
public interface Transformer {
Transformation transform();
}
所以现在这是你的基础界面。 transform
方法不再有参数,而是返回Transformation
。
现在让我们看看如何实施您的B
,C
和D
课程。但首先,请允许我将它们重命名为TransformerB
,TransformerC
和TransformerD
。
此处TransformerB
:
public class TransformerB implements Transformer {
@Override
public Transformation1<String> transform() {
return a -> System.out.println(a); // or System.out::println
}
}
这里重要的是在transform
方法的返回类型中使用协方差。我使用Transformation1<String>
类型,它是Transformation
的子类型,并表示对于TransformerB
类,transform
方法返回接受的转换类型为String
的一个参数。由于Transformation1
接口是SAM类型,我使用lambda表达式来实现它。
以下是如何调用TransformerB.transform
方法中的代码:
TransformerB b = new TransformerB();
b.transform().execute("hello");
b.transform()
返回Transformation1
的实例,其execute
方法会立即调用其预期的String
参数。
现在让我们看看TransformerC
:
public class TransformerC implements Transformer {
@Override
public Transformation2<Integer, Character> transform() {
return (a, b) -> System.out.println(a + b);
}
}
同样,transform
方法的返回类型中的协方差允许我们返回具体的Transformation
,在本例中为Transformation2<Integer, Character>
。
用法:
TransformerC c = new TransformerC();
c.transform().execute(1, 'A');
对于TransformerD
示例,我使用了三参数转换:
public class TransformerD implements Transformer {
public Transformation3<Integer, Double, String> transform() {
return (a, b, c) -> System.out.println(a + b + c);
}
}
用法:
TransformerD d = new TransformerD();
d.transform().execute(12, 2.22, "goodbye");
这是所有类型安全的,因为可以在每个具体TransformationX
方法实现的transform
返回类型中指定泛型类型。但是,它不可能使用原始类型,因为原始类型不能用作泛型类型参数。
关于如何以通用方式调用transform
方法,它很简单:
void callTransf(Transformer a, Object... args) {
a.transform().executeVariadic(args);
}
这就是executeVariadic
方法存在的原因。并且它在每个TransformationX
界面中都被覆盖,因此它可以多态使用,如上面的代码所示。
调用callTransf
方法也很简单:
callTransf(b, "hello");
callTransf(c, 1, 'A');
callTransf(d, 12, 2.22, "goodbye");
答案 2 :(得分:1)
你问的是不可能的。 如果接口方法使用Varargs,那么其他人也必须使用。因此,一种解决方案是让两个类都使用此接口。这是一般性的想法:
public interface A{
public void transform(char ... args);
}
public class B implements A{
public void transform(char ... args){
String s = "";
for(char c : args){
s += c;
}
System.out.println(s);
}
}
public class C implements A{
public void transform(char ... args){
System.out.println(args[0] + args[1]);
}
}
现在,当您在B中调用方法时,必须将字符串转换为char数组:
String str = "example";
char[] charArray = str.toCharArray();
在A中调用方法时,请确保将整数转换为char:
int i = 5;
transform((char)Character.forDigit(i, 10), 'a'); // 10 stands for number radix which is probably 10
这不是完美的解决方案,但它正在运作。
但是没有varargs的更简单的解决方案是使用char数组,但是你需要将输入转换为char数组。
public interface A{
public void transform(char[]);
}
public class B implements A{
public void transform(char[] args){
String s = "";
for(char c : args){
s += c;
}
System.out.println(s);
}
}
public class C implements A{
public void transform(char[] args){
System.out.println(args[0] + args[1]);
}
}
无论如何你做到了,你最终会得到一些复杂的代码,即使使用泛型,你必须记住1方法需要1个参数而另外1个2.我实际上认为最好简单地将这个方法分开
答案 3 :(得分:0)
这是一个非常有趣的问题。如果你知道参数的最大数量,你可以使用方法重载概念。
假设您知道在最多用户可以提供2个参数,那么您可以执行类似的操作。
public void implementation(){
System.out.println("method with zero args")
}
public void implementation(String arg1){
System.out.println("method with one args and is:-"+arg1)
}
public void implementation(String arg1,String arg2){
System.out.println("method with two args and are :-"+arg1+" "+arg2)
}
如果您不知道可以通过多种方式实现的最大args数。 1.创建一个集合并将它们存储在集合对象中,并将该对象作为参数传递。
List args= new List();
l.add(arg1)
----------
----------
----------
l.add(argn)
现在将此作为参数传递给函数调用
objecReference.implementation(l)
2.使用var arg方法。 这是从java 1.8解决此类问题的非常简单的方法。
在实施中
public String implementation(int(change to required datattype)...x){
//here x will act like an array
for(int a:x){//iam assuming int values are coming
System.out.println(a)
}
}
现在您可以使用至少0个args(如
)调用此函数 objecReference.implementation()
objecReference.implementation(10)
objecReference.implementation(10,20)
objecReference.implementation(12,23,34,5,6)
答案 4 :(得分:0)
根据您的要求,您希望在B类和C类中覆盖您的界面中的方法,但是您无法按照自己的方式进行操作。
一种方法是:
public interface A<T> {
public void transform(T ... args);
}
public class B implements A<String> {
@Override
public void transform(String... args) {
}
}
public class C implements A<Integer> {
@Override
public void transform(Integer... args) {
}
}
答案 5 :(得分:0)
一种可能的解决方案是使用标记接口。标记(或标记)接口是一个内部没有方法或常量的接口。它提供有关对象的运行时类型信息。
这是一个使用 Input
接口作为 transform
方法参数的示例。实现此标记接口的类的实例可用作 transform
方法参数。
public interface Input {
}
public interface Transformable {
void transform(Input input);
}
public class InputForA implements Input {
int a;
String b;
public int getA() {
return a;
}
public InputForA setA(int a) {
this.a = a;
return this;
}
public String getB() {
return b;
}
public InputForA setB(String b) {
this.b = b;
return this;
}
}
public class TransformerA implements Transformable {
@Override
public void transform(Input input) {
InputForA inputForA = (InputForA) input;
System.out.println(inputForA.getA() + inputForA.getB());
}
}