Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/variables/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Variables 为什么变量可以';在内联函数中不能像在java中那样正确初始化吗?_Variables_Lambda_Functional Programming_Kotlin - Fatal编程技术网

Variables 为什么变量可以';在内联函数中不能像在java中那样正确初始化吗?

Variables 为什么变量可以';在内联函数中不能像在java中那样正确初始化吗?,variables,lambda,functional-programming,kotlin,Variables,Lambda,Functional Programming,Kotlin,我们知道lambda体是惰性的,因为如果我们不调用lambda,lambda体中的代码就永远不会被调用 我们还知道,在任何函数语言中,变量都可以在函数/lambda中使用,即使它没有初始化,例如javascript、ruby、groovy和.etc。例如,下面的groovy代码可以正常工作: def foo def lambda = { foo } foo = "bar" println(lambda()) // ^--- return "bar" 我们还知道,如果在Java的

我们知道lambda体是惰性的,因为如果我们不调用lambda,lambda体中的代码就永远不会被调用

我们还知道,在任何函数语言中,变量都可以在函数/lambda中使用,即使它没有初始化,例如javascript、ruby、groovy和.etc。例如,下面的groovy代码可以正常工作:

def foo

def lambda = { foo }

foo = "bar"

println(lambda())
//      ^--- return "bar"
我们还知道,如果在Java的try块中引发异常时catch块已初始化变量,则可以访问未初始化的变量,例如:

//  v--- m is not initialized yet
int m;

try{ throw new RuntimeException(); } catch(Exception ex){ m = 2;}

System.out.println(m);// println 2
var a:Int
val lambda = { a }// lambda is never be invoked
//             ^--- a compile error thrown: variable is not initialized yet
a = 2
var a:Int
run{ a = 2 }// a is initialized & inlined to callsite function

//      v--- a compile error thrown: variable is not initialized yet
println(a)
var a: Int

try {
    run { a = 2 }
} catch(ex: Throwable) {
    a = 3
}

//      v--- Error: `a` is not initialized
println(a)
var a: Int = try {
    run { 2 }
} catch(ex: Throwable) {
    3
}

//      v--- println 2
println(a);
val a = run{2};

println(a);//println 2
int a;
try {
    a = 2;
} catch (Throwable ex) {
    a = 3;
}

System.out.println(a); // println 2
如果lambda是惰性的,为什么Kotlin不能在lambda中使用未初始化的变量?我知道Kotlin是一种空安全语言,因此编译器将自上而下分析代码,包括lambda主体,以确保变量已初始化。因此lambda主体在编译时不是“懒惰的”。例如:

//  v--- m is not initialized yet
int m;

try{ throw new RuntimeException(); } catch(Exception ex){ m = 2;}

System.out.println(m);// println 2
var a:Int
val lambda = { a }// lambda is never be invoked
//             ^--- a compile error thrown: variable is not initialized yet
a = 2
var a:Int
run{ a = 2 }// a is initialized & inlined to callsite function

//      v--- a compile error thrown: variable is not initialized yet
println(a)
var a: Int

try {
    run { a = 2 }
} catch(ex: Throwable) {
    a = 3
}

//      v--- Error: `a` is not initialized
println(a)
var a: Int = try {
    run { 2 }
} catch(ex: Throwable) {
    3
}

//      v--- println 2
println(a);
val a = run{2};

println(a);//println 2
int a;
try {
    a = 2;
} catch (Throwable ex) {
    a = 3;
}

System.out.println(a); // println 2
Q:但为什么下面的代码也不能工作?我不明白,因为变量在Java中是有效的final,如果你想更改变量值,你必须使用
ObjectRef
,这个测试与我之前的结论相矛盾:“lambda body在编译时不是惰性的”。例如:

//  v--- m is not initialized yet
int m;

try{ throw new RuntimeException(); } catch(Exception ex){ m = 2;}

System.out.println(m);// println 2
var a:Int
val lambda = { a }// lambda is never be invoked
//             ^--- a compile error thrown: variable is not initialized yet
a = 2
var a:Int
run{ a = 2 }// a is initialized & inlined to callsite function

//      v--- a compile error thrown: variable is not initialized yet
println(a)
var a: Int

try {
    run { a = 2 }
} catch(ex: Throwable) {
    a = 3
}

//      v--- Error: `a` is not initialized
println(a)
var a: Int = try {
    run { 2 }
} catch(ex: Throwable) {
    3
}

//      v--- println 2
println(a);
val a = run{2};

println(a);//println 2
int a;
try {
    a = 2;
} catch (Throwable ex) {
    a = 3;
}

System.out.println(a); // println 2
因此,我唯一能想到的是编译器不能确定
ObjectRef
中的
元素
字段是否已初始化,但@hotkey否定了我的想法为什么

Q:为什么Kotlin内联函数不能正常工作,即使我像在java中一样在catch块中初始化变量?例如:

//  v--- m is not initialized yet
int m;

try{ throw new RuntimeException(); } catch(Exception ex){ m = 2;}

System.out.println(m);// println 2
var a:Int
val lambda = { a }// lambda is never be invoked
//             ^--- a compile error thrown: variable is not initialized yet
a = 2
var a:Int
run{ a = 2 }// a is initialized & inlined to callsite function

//      v--- a compile error thrown: variable is not initialized yet
println(a)
var a: Int

try {
    run { a = 2 }
} catch(ex: Throwable) {
    a = 3
}

//      v--- Error: `a` is not initialized
println(a)
var a: Int = try {
    run { 2 }
} catch(ex: Throwable) {
    3
}

//      v--- println 2
println(a);
val a = run{2};

println(a);//println 2
int a;
try {
    a = 2;
} catch (Throwable ex) {
    a = 3;
}

System.out.println(a); // println 2
但是,@hotkey已经提到,您应该在Kotlin中使用
try catch
表达式来初始化中的变量,例如:

//  v--- m is not initialized yet
int m;

try{ throw new RuntimeException(); } catch(Exception ex){ m = 2;}

System.out.println(m);// println 2
var a:Int
val lambda = { a }// lambda is never be invoked
//             ^--- a compile error thrown: variable is not initialized yet
a = 2
var a:Int
run{ a = 2 }// a is initialized & inlined to callsite function

//      v--- a compile error thrown: variable is not initialized yet
println(a)
var a: Int

try {
    run { a = 2 }
} catch(ex: Throwable) {
    a = 3
}

//      v--- Error: `a` is not initialized
println(a)
var a: Int = try {
    run { 2 }
} catch(ex: Throwable) {
    3
}

//      v--- println 2
println(a);
val a = run{2};

println(a);//println 2
int a;
try {
    a = 2;
} catch (Throwable ex) {
    a = 3;
}

System.out.println(a); // println 2
Q:如果实际情况是这样,为什么我不直接调用
run
?例如:

//  v--- m is not initialized yet
int m;

try{ throw new RuntimeException(); } catch(Exception ex){ m = 2;}

System.out.println(m);// println 2
var a:Int
val lambda = { a }// lambda is never be invoked
//             ^--- a compile error thrown: variable is not initialized yet
a = 2
var a:Int
run{ a = 2 }// a is initialized & inlined to callsite function

//      v--- a compile error thrown: variable is not initialized yet
println(a)
var a: Int

try {
    run { a = 2 }
} catch(ex: Throwable) {
    a = 3
}

//      v--- Error: `a` is not initialized
println(a)
var a: Int = try {
    run { 2 }
} catch(ex: Throwable) {
    3
}

//      v--- println 2
println(a);
val a = run{2};

println(a);//println 2
int a;
try {
    a = 2;
} catch (Throwable ex) {
    a = 3;
}

System.out.println(a); // println 2
但是,上面的代码在java中可以正常工作,例如:

//  v--- m is not initialized yet
int m;

try{ throw new RuntimeException(); } catch(Exception ex){ m = 2;}

System.out.println(m);// println 2
var a:Int
val lambda = { a }// lambda is never be invoked
//             ^--- a compile error thrown: variable is not initialized yet
a = 2
var a:Int
run{ a = 2 }// a is initialized & inlined to callsite function

//      v--- a compile error thrown: variable is not initialized yet
println(a)
var a: Int

try {
    run { a = 2 }
} catch(ex: Throwable) {
    a = 3
}

//      v--- Error: `a` is not initialized
println(a)
var a: Int = try {
    run { 2 }
} catch(ex: Throwable) {
    3
}

//      v--- println 2
println(a);
val a = run{2};

println(a);//println 2
int a;
try {
    a = 2;
} catch (Throwable ex) {
    a = 3;
}

System.out.println(a); // println 2
问:但是为什么下面的代码也不能工作

因为代码可以更改。在定义lambda时,变量未初始化,因此,如果代码发生更改并且lambda随后直接调用,则该变量将无效。kotlin编译器希望确保在初始化未初始化的变量之前,绝对无法访问该变量,即使是通过代理

问:为什么Kotlin内联函数不能正常工作,即使我像java一样在catch块中初始化变量

因为
run
不是特殊的,编译器无法知道主体何时执行。如果你认为代码< >运行< /代码>的可能性没有被执行,那么编译器不能保证变量将被初始化。 在更改后的示例中,它使用try-catch表达式基本上执行
a=run{2}
,这与
run{a=2}
不同,因为返回类型保证了结果

问:如果实际情况是这样,为什么我不直接给跑步打电话


基本上就是这样。关于最终的Java代码,事实是Java并没有遵循Kotlin的完全相同的规则,相反的情况也会发生。仅仅因为Java中的某些东西是可能的,并不意味着它将是有效的Kotlin。

您可以使用以下命令使变量变懒

val a: Int by lazy { 3 }
显然,您可以使用一个函数来代替3。但这允许编译器继续并保证
a
在使用前已初始化

编辑

尽管问题似乎是“为什么不能这样做”。我也有同样的想法,我不明白为什么不能(在合理的范围内)。我认为编译器有足够的信息来确定lambda声明不是对任何闭包变量的引用。因此,我认为当使用lambda并且它引用的变量尚未初始化时,它可能会显示不同的错误

这就是说,如果编译器编写者不同意我的评估(或者花太长时间讨论该特性),我会这样做

下面的示例显示了执行惰性局部变量初始化的方法(对于1.1版和更高版本)

导入kotlin.reflect*
//...
var a:Int by object{
私有变量备份:Int?=null
运算符fun getValue(thisRef:Any?,属性:KProperty):Int=
支持?:抛出异常(“变量尚未初始化”)
运算符fun setValue(thisRef:Any?,属性:KProperty,值:Int){
支持=价值
}
}
var lambda={a}
// ...
a=3
println(“a=${lambda()}”)
我使用了一个匿名对象来显示正在发生的事情(因为
lazy
导致了一个编译器错误)。该对象可以转换为类似于
lazy
的函数


现在,如果程序员在引用变量之前忘记初始化变量,我们可能会返回到运行时异常。但科特林至少尽力帮助我们避免了这种情况。

谢谢您的回答,先生。你的答案和我一样,但为什么我错了?在调用lambda之前,编译器似乎知道“a”是未使用的。更好的错误位置是调用lambda(或传递给某个函数)的位置。我知道结束可能是个问题。@Les Hi。事实上,编译器知道变量
a
是否已初始化,因为编译器必须确保所有内容都已初始化,以支持Kotlin中的空安全性。它必须在编译时自上而下地分析代码。但是为什么代码
运行{a=2}
,我不明白。实际上,在我的第二个问题中,如果没有任何
catch块
,它应该可以正常工作。@kiskae-
run
函数不是特别的,它将运行块并返回,然后
a
将被初始化。这是一个内联函数,否则我同意编译器不必这么做