我遇到了一些Kotlin代码的问题,我发现它与调用一个方法有关,该方法从init块(或辅助构造函数)中分配一些变量,或者重现问题 .
MCVE:
abstract class Shader(/*Input arguments omitted for the sake of an MCVE*/){
init{
//Shader loading and attaching, not relevant
bindAttribs()//One of the abstract methods. In my actual program, this uses OpenGL to bind attributes
//GLSL program validation
getUniforms()//Same as the previous one: abstract method using GL calls to get uniforms. This gets locations so an integer is set (the problem)
}
abstract fun getUniforms();//This is the one causing problems
abstract fun bindAttribs();//This would to if primitives or non-lateinit vars are set
}
abstract class BoilerplateShader() : Shader(){
var loc_projectionMatrix: Int = 404//404 is an initial value. This can be anything though
var loc_transformationMatrix: Int = 404
var loc_viewMatrix: Int = 404
override fun getUniforms(){
//These would be grabbed by using glGetUniformLocations, but it's reproducable with static values as well
loc_projectionMatrix = 0
loc_transformationMatrix = 1
loc_viewMatrix = 2
println(loc_projectionMatrix.toString() + ", " + loc_transformationMatrix + ", " + loc_viewMatrix)
}
//debug method, only used to show the values
fun dump(){
println(loc_projectionMatrix.toString() + ", " + loc_transformationMatrix + ", " + loc_viewMatrix)
}
}
class TextureShader() : BoilerplateShader(){
override fun bindAttribs() {
//This doesn't cause a problem even though it's called from the init block, as nothing is assigned
//bindAttrib(0, "a_position");
//bindAttrib(1, "a_texCoord0");
}
}
//Other repetitive shaders, omitted for brevity
然后做:
val tx = TextureShader()
tx.dump()
打印:
0, 1, 2
404, 404, 404
从getUniforms到最后的转储调用按顺序调用print语句 . 它在 getUniforms
方法中分配得很好,但是在几毫秒之后调用它们时,它们是一个我知道我将不会用于在这个特定MCVE中进行测试的值 .
我正在使用一个严重依赖抽象类的系统,但调用其中一些方法( getUniforms
非常重要)是必须的 . 如果我通过调用 getUniforms
在 BoilerplateShader
或 TextureShader
中添加一个init块,它可以正常工作 . 使用在创建对象后调用的init函数(不是init块)执行变通方法:
fun init(){
bindAttribs();
getUniforms();
}
工作良好 . 但这将涉及创建的实例手动调用它:
val ts = TexturedShader();
ts.init();
ts.dump()
这不是一个选择 . 编写导致Java中Kotlin出现问题的代码就像预期的那样(代码大大缩短,但仍然可以重现):
abstract class Shader{
public Shader(){
getUniforms();
}
public abstract void getUniforms();
}
abstract class BoilerplateShader extends Shader{
int loc_projectionMatrix;//When this is initialized, it produces the same issue as Kotlin. But Java doesn't require the vars to be initialized when they're declared globally, so it doesn't cause a problem
public void getUniforms(){
loc_projectionMatrix = 1;
System.out.println(loc_projectionMatrix);
}
//and a dump method or any kind of basic print statement to print it after object creation
}
class TextureShader extends BoilerplateShader {
public TextureShader(){
super();
}
}
并且在初始化变量和类之后打印变量的值,如预期的那样打印0 .
尝试使用对象重现相同的事物会产生与数字 when the var isn't lateinit 相同的结果 . 所以这:
var test: String = ""
打印:
0, 1, 2, test
404, 404, 404,
最后一行与打印完全一样:默认情况下 test
的值设置为空String,因此显示为空 .
但是如果var被声明为 lateinit var
:
lateinit var test: String
它打印:
0, 1, 2, test
404, 404, 404, test
我can't declare primitives with lateinit . 因为它在构造函数外部调用,所以它需要初始化或声明为 lateinit
.
那么,是否可以从重写的抽象方法初始化基元而无需创建调用它的函数?
编辑:
评论提出了一种工厂方法,但由于抽象,这不会起作用 . 由于尝试的目标是从基类( Shader
)调用方法,并且因为抽象类可以在不在每个类中创建手动实现的情况下工作,这是过度的 . 如果构造函数是私有的以使其工作(避免在工厂方法之外进行初始化),则扩展将不起作用( <init> is private in Shader
) .
因此构造函数被强制公开(无论Shader类是否具有主构造函数或辅助构造函数,子类必须具有初始化它的初始化),这意味着可以在绕过工厂方法的同时创建着色器 . 并且,抽象再次导致问题,工厂方法(必须是抽象的)将在每个子类中手动实现,再次导致初始化并手动调用 init()
方法 .
问题仍然是在从构造函数调用抽象方法时是否可以确保初始化非lateinit和primitives . 如果没有涉及抽象,创建工厂方法将是一个完美的解决方案 .
1 回答
注意:绝对最好的想法是避免在抽象类的构造函数方法中调用抽象函数中声明对象/基元,但有些情况下它很有用 . 尽可能避免使用它 .
我找到的唯一解决方法是使用
by lazy
,因为涉及原语,我可以将赋值转换为块中的工作 .lateinit
会使它稍微容易一些,所以创建对象包装器当然可以是一个选项,但在我的情况下使用by lazy
.无论如何,这里发生的事情是,构造函数中赋值给int的值稍后会被固定值覆盖 . 伪代码:
随着时间的推移,问题被删除:
当我写这个问题时,我认为Java的工作方式不同 . 这是因为我没有在那里初始化变量(有效地,使它们成为lateinit) . 当该类完全初始化时,
int x;
不会被赋值 . 如果它被声明为int x = 1234;
,则Java中出现的问题与此处相同 .现在,问题可以追溯到最近和原始;原始人不能迟到 . 一个相当基本的解决方案是使用数据类:
由于可以解压缩数据类的值:
现在,因为有一个对象的实例(不是原始),lateinit可以使用 . 但是,这并不是特别有效,因为它涉及创建另一个对象 .
唯一剩下的选择:
by lazy
.只要有可能将初始化作为函数创建,这是最佳选择 . 问题中的代码是OpenGL着色器的简化版本(更具体地说,是制服的位置) . 这意味着这个特定的代码很容易转换为
by lazy
块:但视情况而定,这可能不太可行 . 特别是因为
by lazy
需要一个val
,这意味着如果它不会改变就不会出现问题 .