使用lambda提供基于堆栈的上下文(例如,文件操作的路径)

使用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: "*

我想到了一种微妙的方法(可能已经实现并讨论过)来在Groovy中实现这一点(摘自),如果可能的话,我正在尝试改进它:

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中没有任何好处