使用lambda提供基于堆栈的上下文(例如,文件操作的路径)
我想到了一种微妙的方法(可能已经实现并讨论过)来在Groovy中实现这一点(摘自),如果可能的话,我正在尝试改进它:使用lambda提供基于堆栈的上下文(例如,文件操作的路径),lambda,java-8,Lambda,Java 8,我想到了一种微妙的方法(可能已经实现并讨论过)来在Groovy中实现这一点(摘自),如果可能的话,我正在尝试改进它: ant.sequential { echo("inside sequential") def myDir = "target/AntTest/" mkdir(dir: myDir) copy(todir: myDir) { fileset(dir: "src/test") { include(name: "*
ant.sequential {
echo("inside sequential")
def myDir = "target/AntTest/"
mkdir(dir: myDir)
copy(todir: myDir) {
fileset(dir: "src/test") {
include(name: "**/*.groovy")
}
}
echo("done")
}
这个例子很好,但我不是在做一个基于Ant的程序:我想做一些流畅的API来操作文件和目录(我们称之为FileManipular,我对名称不太熟悉:)
基本上是这样的:
new DefaultFileManipulator(Paths.get("root")).with(() -> {
newFile("file1"); // create file root/file1
newFile("file2"); // create file root/file2
cd("directory1", () -> { // create directory root/directory1
newFile("file1"); // create file root/directory1/file1
cd("directory1", () -> { // create direcotry root/directory1/directory1
newFile("file1"); // create file root/directory1/directory1/file1
});
newFile("file2"); // create file root/directory1/file2
});
});
使用以下界面:
interface FileManipulator {
FileManipulator with(LambdaFileManipulator m);
FileManipulator cd(String path, LambdaFileManipulator m);
Path newFile(String path) throws IOException;
}
@FunctionalInterface
interface LambdaFileManipulator extends FileManipulator {
void execute() throws IOException;
default FileManipulator with(LambdaFileManipulator m) {
return FileManipulatorStack.manipulatorFor(this).with(m);
}
// for each non default method of FileManipulator, the same call to FileManipulatorStack.manipulatorFor(this).
}
DefaultFileManipulator
只实现FileManipulator
,并使用下面定义的AbstractFileManipulator
因为我希望路径是相对的,所以我需要以某种方式保持lambda上下文(我不想弄乱当前的工作目录):我使用一个堆栈来实现这一点,堆栈的操作如下:
// visibility package, because that's technical stuff!
class FileManipulatorStack {
private static final Map<LambdaFileManipulator , ArrayDeque<LambdaFileManipulatorDelegator>> stacks = new ConcurrentHashMap<>();
static LambdaFileManipulatorDelegator manipulatorFor(final LambdaFileManipulator delegatee) {
final ArrayDeque<LambdaFileManipulatorDelegator> stack = stacks.get(delegatee);
if (null == stack) {
throw new IllegalStateException("state is empty for [" + delegatee + "]");
}
return stack.getLast();
}
static void delegate(final Path scopedPath, final LambdaFileManipulator delegatee) {
final LambdaFileManipulatorDelegator handler = new LambdaFileManipulatorDelegator(scopedPath);
final ArrayDeque<LambdaFileManipulatorDelegator> stack = stacks.computeIfAbsent(delegatee, key -> new ArrayDeque<>());
stack.addLast(handler);
try {
delegatee.execute();
} catch (final Exception e) {
throw new DelegatedFileCreatorHandlerUndeclaredException(e);
} finally {
final LambdaFileManipulatorDelegator ss = stack.removeLast();
if (ss != handler) {
throw new IllegalStateException("invalid stack");
}
if (stack.isEmpty()) {
stacks.remove(delegatee);
}
}
}
static class LambdaFileManipulatorDelegator extends AbstractFileManipulator {
... constructor ...
}
}
abstract class AbstractFileManipulator implements FileManipulator {
private final Path root;
public AbstractFileManipulator(Path root) {
this.root = requireNonNull(root, "root");
}
public final FileManipulator with(LambdaFileManipulator m) {
FileManipulatorStack.delegate(root, m);
return this;
}
public final FileManipulator cd(String path, LambdaFileManipulator m) {
FileManipulatorStack.delegate(root.resolve(path), m);
return this;
}
public final Path newFile(String path) {
Path p = root.resolve(path);
Files.createFile(p);
return p;
}
}
execute(
new File("/tmp/test"),
newFile("file1"), // create file root/file1
newFile("file2"), // create file root/file2
cd(
"directory1",
newFile("file1"), // create file root/directory1/file1
cd("directory1", // create direcotry root/directory1/directory1
newFile("file1") // create file root/directory1/directory1/file1
),
newFile("file2") // create file root/directory1/file2
)
);
//可见性包,因为这是技术性的东西!
类文件操纵器堆栈{
私有静态最终映射堆栈=新的ConcurrentHashMap();
静态LambdaFileManipulator Delegator Manipulator for(最终LambdaFileManipulator委托人){
最终ArrayQue堆栈=stacks.get(被委派者);
if(null==堆栈){
抛出新的IllegalStateException(“状态对于[“+delegatee+”]”为空);
}
返回stack.getLast();
}
静态无效委托(最终路径scopedPath,最终lambdFileManipulator被委托人){
最终LambdaFileManufactorDelegator处理程序=新的LambdaFileManufactorDelegator(scopedPath);
final ArrayDeque stack=stacks.computeIfAbsent(delegatee,key->new ArrayDeque());
addLast(处理程序);
试一试{
delegatee.execute();
}捕获(最终异常e){
抛出新的DelegatedFileCreatorHandlerUndeclaredException(e);
}最后{
最终LambdaFileManufactorDelegator ss=stack.removeLast();
if(ss!=handler){
抛出新的IllegalStateException(“无效堆栈”);
}
if(stack.isEmpty()){
删除(被授权人);
}
}
}
静态类LambdaFileManufactorDelegator扩展了AbstractFileManufactor{
…构造函数。。。
}
}
抽象类AbstractFileManipular实现FileManipular{
私有最终路径根;
公共抽象文件操纵器(路径根){
this.root=requirennull(root,“root”);
}
带有(lambdafilem)的公共最终文件操纵器{
FileManufactorStack.delegate(根,m);
归还这个;
}
公共最终文件操纵器cd(字符串路径,lambdafilem操纵器){
FileManufactorStack.delegate(根解析(路径),m);
归还这个;
}
公共最终路径newFile(字符串路径){
路径p=根解析(路径);
createFile(p);
返回p;
}
}
关于Stackoverflow规则,我的问题可能不“好”,但以下是:
如何在不向lambda添加文件操纵器
作为参数的情况下改进这一点(我将使用消费者
)
使用lambda作为映射的键是否有问题?(在中,它表示lambda将动态转换为LambdaFileManipulator
的实例,那么我可能不需要堆栈映射)
我是否错过了Java8的一些特性,这些特性允许我将lambda作为某个类的实现方法
编辑:我回答了自己的问题。。。这是行不通的,因为lambda不知道/永远不知道它是
文件操纵器的实现。因此,它不能调用这些方法。它可能使用静态方法(和一些上下文)工作,但我认为这比使用参数更糟糕。我不知道它怎么可能看起来像所示的示例,在前面没有对象的情况下调用newfile()和cd()
它们不是静态函数,封闭类没有理由实现这些函数
我提出了这个不使用lambdas,但可以运行非常接近您的示例的东西
enum FileActionType {
CD, NEWFIlE;
}
class FileAction {
final public String name;
final public FileActionType type;
final public FileAction actions[];
public FileAction(String name, FileActionType type, FileAction[] actions) {
this.name = name;
this.type = type;
this.actions = actions;
}
}
实现静态功能:
public static void execute(File f, FileAction... actions) {
f.mkdirs();
for (int i = 0; i < actions.length; i++) {
FileAction action = actions[i];
switch (action.type) {
case CD:
execute(new File(f, action.name), action.actions);
break;
case NEWFIlE:
try {
Files.createFile(new File(f, action.name).toPath());
} catch (IOException ex) {
}
break;
}
}
}
public static FileAction cd(String name, FileAction... actions) {
return new FileAction(name, FileActionType.CD, actions);
}
public static FileAction newFile(String name) {
return new FileAction(name, FileActionType.NEWFIlE, null);
}
我遇到了一些编译问题,这让我觉得我的想法是可行的:
- lambda仅在需要时才进行浇铸,例如:它使用lambda作为单一抽象方法的实现站点来生成该接口的实现
- 结果表明,Javac和Eclipse都不接受上下文之外的调用
newFile
,cd
。如果使用方法失败,newFile(String)
对于类型filemanufactortest
是未定义的
因此,我将把文件操纵器
作为参数(我试图避免使用该参数)。这正是神奇之处:LambdaFileManipulator
接口使用一个额外的非默认方法(execute
)扩展了文件操纵器
)。另一种方法默认用于获取lambda的伴生对象。所以编译器看到它们是因为lambda实现了一个接口。很抱歉,我错过了cd()和newfile()的默认方法,因为它们只是在注释中提到的。我仍然不确定lambda方法与仅仅传递普通对象相比有什么优势。对静态变量的需求困扰着我,尽管我认为它是可行的。没错,lambda表达式不会从它们最终实现的函数接口继承任何东西。这与lambda表达式中的this
和super
不是指lambda实例,而是指其周围的上下文这一事实是一致的。此外,这些实例具有未指定的标识,因此不适合用作地图中的键。如果操纵器按顺序运行,您可以在后台将上下文信息存储在静态ThreadLocal
中,否则,参数是不可避免的。我添加了参数
,因为即使有上下文,也无法避免使用参数作为调用的前缀。
:main中没有任何好处