精华内容
下载资源
问答
  • Lambda表达式中引用变量的问题

    千次阅读 2020-03-23 13:55:15
    Lambda表达式中引用变量的问题 Lambda表达式内部自定义的变量肯定没问题。引用的外部final变量也没问题。问题在于effectively final变量的理解,及应用场景的认识。引用的外部变量没有加final限定符,只要没有发生过...

    Lambda表达式中引用变量的问题

    Lambda表达式内部自定义的变量肯定没问题。引用的外部final变量也没问题。问题在于effectively final变量的理解,及应用场景的认识。引用的外部变量没有加final限定符,只要没有发生过改变,就可以当作是事实上的final变量。变量没改变过,就是说Lambda表达式引用的外部变量在它所在的作用域范围内,只赋值过一次**,该变量名称只出现过一次**,就算是effectively final。举例说明。
    例1:增强for循环,实质上使用的是迭代器(参见例3)。

    String[] array = {"a", "b", "c"};
            for(Integer i : Arrays.asList(1,2,3)){
                Stream.of(array).map(item -> Strings.padEnd(item, i, '@'))
                        .forEach(System.out::println);
            }
    

    在例1中,虽然for循环代码块中变量 i 的值是变化的,但是 i 只出现过一次,没有明显的改变,也算事实上的final,Lambda表达式能通过。与之相反的示例,看例2。
    例2:普通for循环。

     String[] array = {"a", "b", "c"};
            for(Integer i = 1; i<4; i++){
                Stream.of(array).map(item -> Strings.padEnd(item, i, '@'))
                        .forEach(System.out::println);
            }
    

    在例2中,Lambda表达式引用的外部变量 i 所在的作用域范围是for循环体。明显有两次赋值 i =1 , i++。要报错误提示:Variable used in lambda expression should be final or effectively final。变量 i 不能算是effectively final。

    例3:迭代器。

    String[] array = {"a", "b", "c"};
            List<Integer> list = Arrays.asList(1, 2, 3);
            Iterator<Integer> iterator = list.iterator();
            while(iterator.hasNext() ){
                int i = iterator.next();
                Stream.of(array).map(item -> Strings.padEnd(item, i, '@'))
                        .forEach(System.out::println);
            }
    

    增加for循环也会转为迭代器调用模式。可以明显看出变量 i 只出现过一次有效赋值,没有修改过,算是effectively final。Lambda表达式能通过。

    例4: 将普通for循环变量赋给一个中间变量,可通过。

     String[] array = {"a", "b", "c"};
            for(Integer i = 1; i<4; i++){
                int k = i;
                Stream.of(array).map(item -> Strings.padEnd(item, k, '@'))
                        .forEach(System.out::println);
            }
    

    因为变量 k 的作用域是for循环的一次{},变量 k 在一次{}代码块中只赋值过一次后,没有再被改变过,即effectively final。Lambda表达式能通过。再举两个很近似的例子,就更能说明问题。
    例4-2: 变量的判断不算是被修改。

     String[] array = {"a", "b", "c"};
            for(Integer i = 1; i<4; i++){
                int k = i;
                if(k>2){
                    System.out.println(k);
                }
                Stream.of(array).map(item -> Strings.padEnd(item, k, '@'))
                        .forEach(System.out::println);
            }
    

    该Lambda表达式能通过。因为 k 只有过一次赋值修改后就没有改变过。k 的后两次出现只是读取使用,不是写入。

    例4-3: 自己赋给自己也算是再次被修改。

     String[] array = {"a", "b", "c"};
            for(Integer i = 1; i<4; i++){
                int k = i;
                k = k;
                Stream.of(array).map(item -> Strings.padEnd(item, k, '@'))
                        .forEach(System.out::println);
            }
    

    这个Lambda表达式也会报错。因为变量 k 出现了两次赋值,不符合final的定义。这样多举几个例子测试,就算彻底搞明白了effectively final的含义及应用场景了。

    展开全文
  • Lamda表达式 Lamda表达式是JDK1.8引入的重要技术特征,所谓Lamda表达式指的是应用在单一抽象方法接口环境下的一种简化定义形式,可以用于解决匿名内部类的定义复杂问题。 对于Lamda语法有3种形式: 1.(参数)-> ...

    Lamda表达式
    Lamda表达式是JDK1.8引入的重要技术特征,所谓Lamda表达式指的是应用在单一抽象方法接口环境下的一种简化定义形式,可以用于解决匿名内部类的定义复杂问题。
    对于Lamda语法有3种形式:
    1.(参数)-> 单行代码;
    2.(参数)-> {多行代码};
    3.(参数)->表达式。

    匿名内部类:

    package com.yootk.demo;
    interface IMessage{
    	public void print();
    }
    public class TestDemo{
    	public static void main(String[] args){
    	fun(new IMessage(){
    		@Override 
    		public void print(){
    			System.out.println("更多课程请访问:www.yootk.com";
    			}
    		});
    	}
    	public static void fun(IMessage msg){
    		msg.print();
    		}
    	}
    	程序执行结果:更多课程请访问:www.yootk.com
    

    单行代码:

    package com.yootk.demo;
    interface IMessage{
    	public void print();
    }
    public class TestDemo{
    	public static void main(String[] args){
    //此处为Lamda表达式,没有任何输入参数,只是进行参数输出操作
    fun(()->System.out.println("更多课程请访问:www.yootk.com“));
    }
    public static void fun(IMessage msg){
    	msg.print();
    	}
    }
    程序执行结果:更多课程请访问:www.yootk.com
    

    从对比中可以看到,用了lamda表达式后代码变得更加简洁了。

    多行代码:

    package com.yootk.demo;
    @FunctionallInterface 
    //为了分辨出Lamda表达式的使用接口,可以在接口上使用"@FunctionallInterface"注解声明,
    //这样就表示此为函数式接口,里面只允许定义一个抽象方法。
    interface IMessage{
    	public void print();
    }
    public class TestDemo{
    	public static void main(String[] args){
    		String info = "魔乐科技软件学院:www.mldn.cn";
    	fun(() -> {
    			System.out.println("更多课程请访问:www.yootk.com";
    			System.out.println(info);//输出方法中的变量
    		});
    	}
    	public static void fun(IMessage msg){
    		msg.print();
    		}
    	}
    	程序执行结果:  更多课程请访问:www.yootk.com
    				 魔乐科技软件学院:www.mldn.cn
    

    定义有参数有返回值的方法:

    package com.yootk.demo;
    @FunctionallInerface
    interface IMessage{
    	public int add(int x,int y);
    }
    public class TestDemo{
    	public static void main(String[] args){
    //此处为Lamda表达式,没有任何输入参数,只是进行参数输出操作
    fun((s1,s2) -> {
    	return s1 + s2;
    	});
    }
    public static void fun(IMessage msg){
    	System.out.println(msg.add(10,20));
    	}
    }
    执行结果: 20
    

    方法引用:
    在Java中利用对象的引用传递可以实现不同的对象名称操作同一块堆内存空间,而从JDK1.8开始,在方法上也支持了引用操作,这样就相当于定义了别名。对于方法引用,Java一共定义了以下4种操作形式。
    1.引用静态方法:类名称 :: static方法名称;
    2.引用某个对象的方法:实例化对象 :: 普通方法;
    3.引用特定类型的方法:特定类 :: 普通方法;
    4.引用构造方法:类名称 :: new;

    函数式接口
    1.功能型接口(Function)
    public interface Function<T,R> {
    public R apply(T t);
    }
    此接口需要接收一个参数,并且返回一个处理结果

    package com.yootk.demo;
    import java.util.function.Function;
    public class TestDemo{
    	public static void main(String[] args){
    		Function<String,Boolean> fun = "##yootk" :: startsWith;
    		System.out.println(fun.apply("##"));//相当于利用对象调用startWith()
    		}
    	}
    程序执行结果:true
    

    2.消费型接口(Consumer)
    public interface Consumer {
    public void accept(T t);
    }
    此接口只是负责接收数据(引用数据时,不需要返回),并且不返回处理结果。

    package com.yootk.demo;
    import java.util.function.Consumer;
    public class TestDemo {
    	public static void main(String[] args){
    		Consumer<String> cons = System.out :: print;
    		cons.accept("更多课程,请访问:www.yootk.com");
    		}
    	}
    程序执行结果:更多课程,请访问:www.yootk.com
    

    3.供给型接口(Supplier)
    public interface Predicate{
    public boolean get();
    }
    此接口不接收参数,但可以返回结果。

    package com.yootk.demo;
    import java.util.function.Supplier;
    public class TestDemo{
    	public static void main(String[] args){
    		Supplier<String> sup = "yootk":: toUpperCase;
    		System.out.println(sup.get());
    		}
    	}
    程序执行结果:YOOTK
    

    4.断言型接口(Predicate)
    public interface Predicate{
    public boolean test(T t);
    }
    进行判断操作使用。

    package com.yootk.com.demo;
    import java.util.function.Predicate;
    public class TestDemo{
    	public static void main(String[] args){
    		Predicate<String> pre = "yootk" :: equalsIgnoreCase;
    		System.out.println(pre.test("YOOTK"));
    		}
    	}
    程序执行结果:true
    
    展开全文
  • F.52: Prefer capturing by reference in lambdas that ...F.52 在lambda表达式中使用引用形式捕捉局部变量,包含向算法传递变量的情况。 译者注:如果你不清楚捕捉(capture)是什么,请参考 https://mp.weixin...

    F.52: Prefer capturing by reference in lambdas that will be used locally, including passed to algorithms

    F.52 在lambda表达式中使用引用形式捕捉局部变量,包含向算法传递变量的情况。

     

    译者注:如果你不清楚捕捉(capture)是什么,请参考

    https://mp.weixin.qq.com/s/VuW_5OcX7uUUrMbsN2wxPQ

    Reason(原因)

    For efficiency and correctness, you nearly always want to capture by reference when using the lambda locally. This includes when writing or calling parallel algorithms that are local because they join before returning.

    为了效率和正确性,在本地使用lambda表达式时,你差不多总是需要通过引用方式捕捉变量。这包括编写或者调用本地并发算法的情况,因为它们需要在返回前汇合(从而保证安全性,译者注)。

    Discussion(讨论)

    The efficiency consideration is that most types are cheaper to pass by reference than by value.

    关于效率的考虑:对于大多数类型来说,传引用都会比传值的成本更低。

     

    The correctness consideration is that many calls want to perform side effects on the original object at the call site (see example below). Passing by value prevents this.

    关于正确性的考虑:许多调用都希望(在被调用函数被执行的同时,)对处于调用侧的原始对象产生连带效果(参考下面的示例)。传值做不到这一点。

    Note(注意)

    Unfortunately, there is no simple way to capture by reference to const to get the efficiency for a local call but also prevent side effects.

    不幸的是,对于局部调用,不存在简单的方法可以通过引用捕捉常量类型变量获得效率又可以避免连带效果。

     

    译者注:常量类型应用应该指的是在函数声明中的被声明为常量的引用参数,通过这种方式可以同时提供高效率和写保护。

    Example(示例)

    Here, a large object (a network message) is passed to an iterative algorithm, and is it not efficient or correct to copy the message (which may not be copyable):

    这里,一个大对象(一个网络消息)传递给一个迭代算法,拷贝该消息不是高效或者正确的做法(它也许不能拷贝):

     

    std::for_each(begin(sockets), end(sockets), [&message](auto& socket){    socket.send(message);});

    译者注:每次循环都拷贝的代价也很高。

     

    Example(示例)

    This is a simple three-stage parallel pipeline. Each stage object encapsulates a worker thread and a queue, has a process function to enqueue work, and in its destructor automatically blocks waiting for the queue to empty before ending the thread.

    这是一个简单的三阶段并发管道。每个state对象封装一个工作线程和一个队列,有一个process函数处理入队,它的析构函数在关闭线程之前自动地进入阻塞状态以等待队列变空。

    
     

    ​​​​​​​

    void send_packets(buffers& bufs){    stage encryptor([] (buffer& b){ encrypt(b); });    stage compressor([&](buffer& b){ compress(b); encryptor.process(b); });    stage decorator([&](buffer& b){ decorate(b); compressor.process(b); });    for (auto& b : bufs) { decorator.process(b); }}  // automatically blocks waiting for pipeline to finish

    译者注:代码中使用的是隐式捕捉,具体可以参考:https://mp.weixin.qq.com/s/RNpLqwekN3BJUbIN-DwVxQ

     

    Enforcement(实施建议)

    Flag a lambda that captures by reference, but is used other than locally within the function scope or passed to a function by reference. (Note: This rule is an approximation, but does flag passing by pointer as those are more likely to be stored by the callee, writing to a heap location accessed via a parameter, returning the lambda, etc. The Lifetime rules will also provide general rules that flag escaping pointers and references including via lambdas.)

    标记lambda表达式采用引用方式捕获变量,但是没有在函数范围内本地使用,或者通过引用方式传递给另外一个函数的情况。

    注意:这是一个近似的规则,但是确实可以标记出:通过很有可能被调用者保存的指针进行的传递,对通过参数访问的堆上的某处进行的写入,返回lambda表达式等等。生命周期准则群组也会提供标记逃逸指针和参照的普遍准则,也会包含源自lambda表达式的情况。

     

    觉得本文有帮助?欢迎点赞并分享给更多的人!

    阅读更多更新文章,请关注微信公众号【面向对象思考】

    展开全文
  • lamda表达式

    2020-07-28 14:50:38
    C++ lamda表达式 lamda表达式是C++11中的新特征,说白了就是匿名函数。 lambda表达式的具体形式如下...capture list:捕获外部变量列表,即需要用到的外部变量。 params list:形参列表 mutable指示符:用来说...

    C++ lamda表达式

        lamda表达式是C++11中的新特征,说白了就是匿名函数。


        lambda表达式的具体形式如下:        

    [capture list] (params list) mutable exception-> return type { function body }

    例: 

    各项具体含义如下 

    1. capture list:捕获外部变量列表,即需要用到的外部变量。
    2. params list:形参列表
    3. mutable指示符:用来说用是否可以修改捕获的变量
    4. exception:异常设定
    5. return type:返回类型
    6. function body函数体

    此外,我们还可以省略其中的某些成分来声明“不完整”的Lambda表达式,常见的有以下几种:

    序号 格式
    1 [capture list] (params list) -> return type {function body}
    2 [capture list] (params list) {function body}
    3 [capture list] {function body}

    其中:

    • 格式1声明了const类型的表达式,这种类型的表达式不能修改捕获列表中的值。
    • 格式2省略了返回值类型,但编译器可以根据以下规则推断出Lambda表达式的返回类型:

              (1):如果function body中存在return语句,则该Lambda表达式的返回类型由return语句的返回类型确定;

              (2):如果function body中没有return语句,则返回值为void类型。

    • 格式3中省略了参数列表,类似普通函数中的无参函数。

    来个最简单的:

    [](){};

      ”[ ]"表示不需要使用外部变量,”( )"表示形参列表为空,"{ }"表示空函数,什么都不干。

        再举一个例子。
        在STL中有一个排序函数std::sort,如果现在我们要用它来按降序排列一个数组arr,则可自定义一个比较函数cmp,并把它传给sort函数,如下:

    bool cmp(int a, int b)
    {
        return a > b;   
    }
    
    std::sort(arr, arr + len, cmp);

    如果使用lamda表达式,则可改写成如下

    std::sort(arr, arr + len, [](int a, int b) 
        {
            return a > b;
       });

    "[ ]"明没有用到外部变量,如果我们要用到外部变量,则要对"[ ]"进行修改,如下代码

    int n = 10;
    
    [](int a) {return a + n;}; // 报错,提示变量n不能在lamda体中调用,除非其在捕获列表中

           "[ ]"捕获列表是指,如果我们想要访问n,在"[ ]"中加上n即可。如下

    int n = 10;
    
    [n](int a) {return a + n;}; // 正确

      "[ n ]"说明n是以传值的方式传到lamda体中,传值和传引用的差别请自行百度。捕获列表可按如下规则传入

    • [ ] ——什么也没有捕获
    • 值捕获:[ a ] ————按值捕获a
    • 引用捕获:[ & ] ——按引用捕获任何用到的外部变量
    • 隐式捕获:[ = ] ——按值捕获任何用到的外部变量

    下面为混合方式

    • [ a,  &b ] ————按值捕获a,按引用捕获b
    • [ a,  & ] ————按值捕获a, 其它的变量按引用捕获
    • [ &b, = ] ————按引用捕获b,其它的变量按值捕获
    • [ this ] ————按值捕获this指针

      注意: 假如我们想修改按值传进来的形参,将会发生错误,因为按值捕获的对象不能被修改,如下代码

    int n = 10;
    [ n ](int a) 
    {
        n += a; // 报错,提示不允许修改n的值
        return n;
    };

         这时我们需要假如一个mutable,如下

    int n = 10;
    [ n ](int a) mutable 
    {
        n += a; // 正确
        return n;
    };

     

     

    关于返回值类型,可由以下规则推导出来

    • 如果body仅包含单一的return语句,那么返回值类型是返回表达式的类型(在此隐式转换之后的类型:右值到左值、数组与指针、函数到指针)
    • 否则,返回类型是void

      譬如我们上面std::sort()的这个例子,lamda表达式是

    [](int a, int b) {return a > b;};

      a > b 这个表达式返回的类型是bool,因此这个lamda表达式返回类型是bool。

      最后一个例子,我们可以把lamda表达式进行赋值,还是利用std::sort()这个例子。我们可以把cmp函数写成如下

    std::function<bool(int, int)> cmp = [](int a, int b) {return a > b;};  
        //std::function包含在头文件functional中,具体用法请自行百度

      这样做的好处是可以多次利用lamda表达式。当然,我们可以指定其返回类型为auto,来让其自行判断。

    auto cmp = [](int a, int b) {return a > b;};

     

    整理于:

    https://www.cnblogs.com/IamTing/p/4541901.html

    https://www.cnblogs.com/zhuyp1015/archive/2012/04/08/2438176.html

    https://blog.csdn.net/datase/article/details/80075241

    展开全文
  • Lamda表达式

    2021-01-26 21:04:25
    C++11中的Lamda表达式用于定义并创建匿名的函数对象,以简化编程工作。 [capture](parameters)mutable->...函数对象参数只能使用那些到定义lamda为止时所在作用范围内可见的局部变量(包括lamda所在类的thi
  • lamda 表达式

    2016-06-30 16:35:00
    lamda演算包括一条变换规则 (变量替换) 和一条函数定义方式, 通过带入和替换, 对输入产生输出。 Connect 新用法 connect 连接 信号槽 connect(sender, &Sender::valueChanged, receiver, &...
  • • 面向过程程序语言:参数传递是基本类型的变量 • 面向对象语言 – 传递基本类型的变量 – 传递对象变量 • 传递方法/代码块(函数式程序语言设计) – 刚开始,Java为了简单性、一致性,拒绝此功能 – 为了市场和...
  • C++ lamda

    2020-01-01 16:36:42
    lamda lamda表达式是c++11规范引入的新语法。“Lambda 表达式”(lambda expression)是一个匿名函数,Lambda表达式基于数学中的λ演算得名,直接对应于其中的lambda抽象(lambda abstraction),是一个匿名函数,即没有...
  • lambda表达式只能引用外部final变量

    千次阅读 2019-03-11 16:38:13
    捕获实例或静态变量是没有限制的(可认为是通过 final 类型的局部变量 this 来引用前两者) 捕获的局部变量必须显式的声明为final或实际效果的的final类型 回顾一下我们在 Java 8 之前,匿名类中如果要访问局部变量的...
  • C++11 lamda表达式

    2018-03-23 16:32:34
    lamda表达式可以被认为是一个匿名的函数,之前不明白为什么C++11中会加入这个特性?匿名的函数能带来什么好处?有名的函数只不过增加了一些函数调用开销而已,无伤大雅。下面开始走进lamda函数的世界。基本的Lamda...
  • C++ lamda表达式

    千次阅读 2016-03-23 13:56:24
    简要介绍了c++中的lamda表达式和其用法
  • 在一个方法内部使用lambda表达式,如果表达式中操作了外部方法的局部变量,需要将外部方法的局部变量定义成final类型的,为什么会这样?带着在这个问题网上搜了一下多种答案,也没说太明白。最近看了JVM相关的内容...
  • Lambda表达式对dest添加对象,后续对dest变量修改(非内容修改),报错! List<String> dest = new ArrayList<>(); List<String> src = new ArrayList<>(Arrays.asList("01", "02", "03")...
  • jdk1.8 lambda变量引用

    2019-05-19 15:21:53
    方法引用 若lambda体中的内容有方法已经实现了,那么可以使用“方法引用” 也可以理解为方法引用是lambda表达式的另外一种表现形式并且其语法比lambda表达式更加简单 (a) 方法引用 三种表现形式: 对象::实例...
  • C++ Lamda表达式

    2020-05-28 00:47:10
    Lamda表达式作用: Lambda表达式类似Javascript中的闭包,它可以用于创建并定义匿名的函数对象,以简化编程工作。 Lambda的语法如下: [函数对象参数](操作符重载函数参数)->返回值类型{函数体} vector<int&...
  • Lambda 表达式引用的局部变量必须是最终变量或实际上的最终变量,也就是说局部变量在被创建后不得被重新赋值。 分析 那么为什么呢?我看了网上的许多文章,写的都比较模糊,例如: 它所能访问到的外部类中的各种...
  • 这是因为lambda函数内使用外部变量时,并不是使用引用,而是把变量的值拷贝了一份放到自己作用域中。所以如果变量之后被修改,会造成混乱,因此外部变量虽然不需要用final修饰,但也不允许被修改。 ...
  • lamda expression

    2019-04-23 16:25:00
    因为内部类通过继承得到的成员(包括来自Object的方法)可能会把外部类的成员隐藏起来(shadow),同时未限定(unqualified)的this引用会指向内部类自己而非外部类,这时想要在内部中引用外部内的成员时,需要做...
  • Lamda Expression

    2019-04-21 19:33:00
    使用lambda表达式,您可以引用任何最终变量或有效的最终变量(仅分配一次)。 如果第二次为变量赋值,则Lambda表达式会抛出编译错误。 范围示例 使用您选择的任何编辑器创建以下Java程序,例如C:\> JAVA。 ...
  • C++11(二):lamda表达式

    千次阅读 多人点赞 2017-02-28 17:52:31
    前言lamda表达式是c++11规范引入的新语法,我最早接触lamda表达式是在C#语言中,后来学习python的过程中渐渐发现这种语法的好处,实际上它就是一个匿名函数,如果你的代码里有一些只用了一次的小函数,不妨试试用...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 3,407
精华内容 1,362
关键字:

lamda引用变量