我有一个通用方法(称为map
),它采用Funcn
类型的参数,其中Funcn<K,T>
是一个只有一个方法T eval(K)
的接口, K
并返回T
。在map
中,我需要使用Funcn
来迭代List<K>
(在main
方法中定义 - 我在{{1}中使用this
访问它)并将map
应用于列表中的每个元素。最后,应返回Funcn
的所有结果的新List<T>
。但是,我不确定如何做到这一点,因为我不熟悉泛型的语法。
eval
答案 0 :(得分:1)
在您的代码中,接口Funcn<K,T>
声明了一个方法eval
,它将K
作为参数并返回T
。请注意,K
是第一个类型参数,T
是第二个。
public interface Funcn <K,T> {
public T eval(K k);
}
在您的map
方法的声明中,您已将Funcn
的类型参数反转:
public java.util.ArrayList<T> map(Funcn<T,K> fn){ /* … */ }
// * *
这意味着fn.eval
需要T
并返回K
。相反,它应为Funcn<K,T> fn
,以便fn.eval
获取K
并返回T
。这将解释您在评论中提到的错误消息:“类型apply(K)
中的方法Function<T,K>
不适用于参数(T)
”(这不能解释您为什么这么做'但是,我在一个地方获得了Function
而在另一个地方获得了Funcn
。您是否向我们展示了真实的代码?)
交换这些参数的顺序将解决您的直接问题,但通常类型参数有点复杂。让这些泛型函数的输入和输出类型完全正确是很棘手的,因为你应该能够映射一个函数,该函数需要一个类型为C的参数,而该列表的元素属于C的子类型。代码中的注释解释了这个更详细一点。
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class MapExample {
/**
* A Function maps an input to an output.
*/
interface Function<InputType,OutputType> {
OutputType call( InputType input );
}
/**
* Map returns a list of elements obtained by applying the function
* to each element of the input list. The input and output types
* of the function do not need to align exactly with the type of
* elements in the input list; but the function's input type must
* be some supertype of the element type of the input list. Similarly,
* the output type of the function does not need to be the element type
* of the output list; but it must extend that type so that the result
* of the function can be stored in the output list.
*/
public static <OutputType,InputType> List<OutputType> map(
final Function<? super InputType,? extends OutputType> function,
final List<? extends InputType> list ) {
final List<OutputType> results = new ArrayList<>( list.size() );
for ( InputType in : list ) {
results.add( function.call( in ));
}
return results;
}
public static void main(String[] args) {
// f takes an integer n to the string "*<n+1>*" (where <n+1> is the value of n+1).
Function<Integer,String> f = new Function<Integer, String>() {
@Override
public String call(Integer input) {
return "*"+new Integer( input + 1 ).toString()+"*";
}
};
System.out.println( map( f, Arrays.asList( 1, 3, 6, 8 )));
}
}
输出结果为:
[*2*, *4*, *7*, *9*]
顺便说一句,我发现其中一些功能问题是在对象中使用实例初始化块的好地方。虽然上面的实现创建了results
列表,然后填充,然后返回它,您也可以将map
的主体设为:
return new ArrayList<OutputType>( list.size() ) {{
for ( final InputType in : list ) {
add( function.call( in ));
}
}};
我有点喜欢,虽然您会收到关于新(匿名)类没有序列化ID这一事实的警告,因此您需要@SuppressWarnings("serial")
map
{{1}} 1}}方法,或在类中添加ID。不过,那些可能并不那么令人满意。不过,正如Efficiency of Java "Double Brace Initialization"?中讨论的那样,这类对象也存在其他问题。
答案 1 :(得分:0)
我假设T是 Funcn 的输入,K是它的返回类型。然后它必须返回一个K列表才能工作,否则 Funcn 的通用签名就没用了。
public java.util.ArrayList<K> map(Funcn<T,K> fn){
ArrayList<K> lst = new ArrayList<K>();
for(T value:this){
lst.add( fn.eval(value) );
}
return lst;
}