【问题标题】:How Lambda operator () -> works?Lambda 运算符 () -> 如何工作?
【发布时间】:2019-01-25 04:39:45
【问题描述】:

我想了解一下下面的代码是如何工作的。

@Bean
public StateHandlerDef handler() {
    return () -> StateOne.class;
}

@Named
@Scope("prototype")
public class StateOne extends AbstractStateActorActor<StatObject> {

    @Inject
    public StateOne(final Props prop, final StatRegistry statRegistry) {
        super("test", transformationConfig, GenericStateObject.class, statRegistry);
    }
}

@FunctionalInterface
public interface StateHandlerDef {
    Class<? extends AbstractUntypedActor> getHandlerClass();
}

这是工作代码。

我想了解,这里的 bean 创建是如何工作的。

以下代码创建一个 bean。

@Bean
public StateHandlerDef handler() {
    return () -> StateOne.class;
}

StateOne 类有一个构造函数。但是,这会在不传递构造函数参数的情况下创建 bean。此外,返回类型是一个函数接口,实际状态类没有实现,也不知道它是如何工作的。这是基于 Akka 演员模型。

AbstractStateActorActor extends AbstractUntypedActor

在这里,我想以编程方式设置 bean 名称,而不是通过注解设置。

@Bean("测试")

如果我尝试使用 BeanPostProcessor 以编程方式设置 bean 名称,则会引发错误,即无法使用 new 创建实例,应该使用 actorof 创建实例。

Caused by: akka.actor.ActorInitializationException: You cannot create an instance of [com.test.Test] explicitly using the constructor (new). You have to use one of the 'actorOf' factory methods to create a new actor. See the documentation.
    at akka.actor.ActorInitializationException$.apply(Actor.scala:181) ~[akka-actor_2.11-2.4.19.jar:na]

有什么帮助吗?

【问题讨论】:

    标签: java spring akka


    【解决方案1】:

    要理解这一点,请这样想。您尝试扩展的库(在本例中为 akka)需要知道要处理状态的类。为此,它会获得一个StateHandlerDef 类型的实例(bean)。这个实例是由这段代码中的 lambda 表达式创建的:

    @Bean
    public StateHandlerDef handler() {
        return () -> StateOne.class;
    }
    

    相当于:

    @Bean
    public StateHandlerDef handler() {
        return new StateHanderDefImpl();
    }
    

    库将使用它来获取StateOne.class,它会为此寻找一个bean,并从依赖注入框架中获取它。该 bean 在这里定义:

    @Named
    @Scope("prototype")
    public class StateOne extends AbstractStateActorActor<StatObject> {
    
        @Inject
        public StateOne(final Props prop, final StatRegistry statRegistry) {
            super("test", transformationConfig, GenericStateObject.class, statRegistry);
        }
    }
    

    DI 框架将通过在其构造函数中注入所需的依赖项,从此类创建一个 bean。

    【讨论】:

    • 我如何以编程方式更改 bean 名称而不像 @Bean("") 那样进行硬编码
    • 您需要更改哪个bean名称? StateOneStateHandlerDef?
    • @Bean public StateHandlerDef handler()
    • 为此,我怀疑您需要了解 akka 的内部工作原理以及它如何连接这些 bean。很抱歉我不熟悉akka 为您提供解决方案。
    【解决方案2】:

    @FunctionInterface 是一种特殊类型的接口,它实际上限制用户不能包含多个 SAM(单一抽象方法)。对于下面的示例,我们有一个方法,它将提供任何扩展 Object 类的类。

    @FunctionalInterface
    interface ClassHandleDef {
      Class<? extends Object> getHandlerClass();
    }
    

    现在我们正在创建接口 ClassHandleDef 的匿名类并提供 gethandlerClass 方法的主体。

    new ClassHandleDef() {
            @Override
            public Class<? extends Object> getHandlerClass() {
                return String.class;
            }
        };
    

    现在我们正在删除不需要的额外代码。根据 lambda 表达式删除所有额外代码并提供参数(如果存在)和方法的主体定义以及 lambda 运算符。

    () -> String.class;
    
    • 如果有单行方法定义,则无需显式编写返回语句。
    • 如果只有一个参数,则不需要括号。例如

      a -> a*2;

    希望您了解 lambda 表达式的工作流程。感谢您花时间阅读这篇文章。

    【讨论】:

      【解决方案3】:

      StateHandlerDef 的函数接口以某种方式表示 getter 函数 getHandlerClass(),该函数在其类定义中使用 lambda 定义进行定义。使用以下声明;

      @Bean
      public StateHandlerDef handler() {
          return () -> StateOne.class; // a supplier, no input, returns value (getter)
      }
      

      我们实质上是通过定义getHandlerClass() 方法来实现StateHandlerDef 接口。这就是为什么 lambda 的返回值与 getter 方法相同,StateOne 的类型为 Class&lt;? extends AbstractUntypedActor&gt;

      所以在某种程度上,我们创建的 bean 如下所示;

      public interface StateHandlerDef {
          Class<? extends AbstractUntypedActor> getHandlerClass();
      }
      
      public class StateHandlerDefImpl implements StateHandlerDef {
      
          // explicit way of writing lambda "() -> StateOne.class"
          Class<? extends AbstractUntypedActor> getHandlerClass() {
              return StateOne.class;
          }
      }
      
      @Bean
      public StateHandlerDef handler() {
          return new StateHandlerDefImpl();  // then we use the getter thru this bean.
      }
      

      使用@FunctionalInterface,我们可以跳过上面给出的接口的实现,只需将接口本身与传递的lambda(即Supplier)一起使用。

      现在你可以这样做了;

      @Autowire
      private StateHandlerDef handler;
      
      public .. someLogic() {
          ...
          handler.getHandlerClass();  // will trigger the lambda, returning `StateOne.class`
          ...
      }
      

      您可以通过更改其创建方法的名称来更改 bean 的名称。 @Bean handler() 将生成名称为 handler 的 bean。

      我尽量简化,希望可以理解,否则请查看this

      【讨论】:

      • 构造函数参数怎么样?
      • @user1578872 什么构造函数参数? getter 的返回类型是Class,而不是StateOneClass 类型不需要构造函数,可以使用StateOne.class。它已经是Class&lt;? extends AbstractUntypedActor&gt; 类型了。
      • 我需要传递参数 public StateOne(final Props prop, final StatRegistry statRegistry) 来实例化类。
      猜你喜欢
      • 2020-01-29
      • 2011-02-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-10-25
      • 2017-11-30
      相关资源
      最近更新 更多