这个问题仅是出于我的好奇心,所以我想得到一个完整的答案,而不是简单的“是”或“否”。
让我们考虑这段代码:
// Is stored in util files and used to omit annoying (this as? Smth)?.doSmth()
inline fun <reified T> Any?.cast(): T? {
return this as? T
}
class PagingOnScrollListener(var onLoadMore: (currentPage: Int, pageSize: Int) -> Unit) : RecyclerView.OnScrollListener() {
constructor() : this({ _, _ -> Unit })
private var loading = false
private var currentPage = 0
private var latestPageSize = -1
var visibleThreshold = VISIBLE_THRESHOLD_DEFAULT
var pageSize = PAGE_SIZE_DEFAULT
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
val linearLayoutManager = recyclerView.linearLayoutManager
val totalItemCount = linearLayoutManager.itemCount
val lastVisibleItem = linearLayoutManager.findLastVisibleItemPosition()
if (!loading && totalItemCount - lastVisibleItem <= visibleThreshold
&& latestPageSize !in 0 until pageSize) {
currentPage++
loading = true
onLoadMore(currentPage, pageSize)
}
}
private inline val RecyclerView.linearLayoutManager
get() = layoutManager?.cast<LinearLayoutManager>()
?: throw IllegalStateException("PagingOnScrollListener requires LinearLayoutManager to be attached to RecyclerView!")
companion object {
private const val VISIBLE_THRESHOLD_DEFAULT = 4
private const val PAGE_SIZE_DEFAULT = 10
}
}
[当我在AndroidStudio中使用“显示Kotlin字节码”工具,然后单击“反编译”按钮时,我看到此Java代码(我删除了一些不相关的内容:]
public final class PagingOnScrollListener extends RecyclerView.OnScrollListener {
private boolean loading;
private int currentPage;
private int latestPageSize;
private int visibleThreshold;
private int pageSize;
@NotNull
private Function2 onLoadMore;
private static final int VISIBLE_THRESHOLD_DEFAULT = 4;
private static final int PAGE_SIZE_DEFAULT = 10;
public PagingOnScrollListener(@NotNull Function2 onLoadMore) {
Intrinsics.checkParameterIsNotNull(onLoadMore, "onLoadMore");
super();
this.onLoadMore = onLoadMore;
this.latestPageSize = -1;
this.visibleThreshold = 4;
this.pageSize = 10;
}
public PagingOnScrollListener() {
this((Function2)null.INSTANCE);
}
public void onScrolled(@NotNull RecyclerView recyclerView, int dx, int dy) {
Intrinsics.checkParameterIsNotNull(recyclerView, "recyclerView");
super.onScrolled(recyclerView, dx, dy);
int $i$f$getLinearLayoutManager = false;
RecyclerView.LayoutManager var10000 = recyclerView.getLayoutManager();
if (var10000 != null) {
Object $this$cast$iv$iv = var10000;
int $i$f$cast = false;
var10000 = $this$cast$iv$iv;
if (!($this$cast$iv$iv instanceof LinearLayoutManager)) {
var10000 = null;
}
LinearLayoutManager var10 = (LinearLayoutManager)var10000;
if (var10 != null) {
LinearLayoutManager linearLayoutManager = var10;
int totalItemCount = linearLayoutManager.getItemCount();
int lastVisibleItem = linearLayoutManager.findLastVisibleItemPosition();
if (!this.loading && totalItemCount - lastVisibleItem <= this.visibleThreshold) {
int var11 = this.pageSize;
int var12 = this.latestPageSize;
if (0 <= var12) {
if (var11 > var12) {
return;
}
}
int var10001 = this.currentPage++;
this.loading = true;
this.onLoadMore.invoke(this.currentPage, this.pageSize);
}
return;
}
}
throw (Throwable)(new IllegalStateException("EndlessOnScrollListener requires LinearLayoutManager to be attached to RecyclerView!"));
}
}
这里我们可以看到一些奇怪的代码:
1。
// in constructor:
Intrinsics.checkParameterIsNotNull(onLoadMore, "onLoadMore");
super();
Java要求super
调用是构造函数主体中的第一条语句。
2。
this((Function2)null.INSTANCE);
对应于constructor() : this({ _, _ -> Unit })
null.INSTANCE
是什么意思?为什么没有预期的匿名对象?
this(new Function2() {
@Override
public Object invoke(Object o1, Object o2) {
return kotlin.Unit.INSTANCE;
}
});
3。
方法@Override
上没有onScrolled
注释。使用override
修饰符在方法中添加注释是否太难了?但是,存在@NonNull
和@Nullable
批注。
4。
int $i$f$getLinearLayoutManager = false;
[Boolean
]值已分配给int
变量?为什么这条线出现在这里?此变量无用。为什么它声明一个将不使用的变量?
5。
RecyclerView.LayoutManager var10000 = recyclerView.getLayoutManager();
if (var10000 != null) {
Object $this$cast$iv$iv = var10000; // what's the purpose of this assignment?
int $i$f$cast = false;
var10000 = $this$cast$iv$iv; // Incompatible types. RecyclerView.LayoutManager was expected but got Object.
...
6。
if (!this.loading && totalItemCount - lastVisibleItem <= this.visibleThreshold) {
int var11 = this.pageSize;
int var12 = this.latestPageSize;
if (0 <= var12) {
if (var11 > var12) {
return;
}
}
...
}
为什么不使它变得更简单?
if (!this.loading && totalItemCount - lastVisibleItem <= this.visibleThreshold && (0 > this.latestPageSize || this.pageSize < this.latestPageSize))
7。
// Unhandled exception: java.lang.Throwable.
throw (Throwable)(new IllegalStateException("EndlessOnScrollListener requires LinearLayoutManager to be attached to RecyclerView!"));
如果我们知道IllegalStateException
,为什么将Throwable
强制转换为IllegalStateException extends Throwable
?目的是什么?
是真的在生产中执行的代码还是Java Decompiler不能弄清所有这些东西?
您的大多数问题都可以用Java!= Java字节码来回答。编译从Java删除了很多仅在编译时需要的信息,并且字节码格式还支持许多在Java级别无效的内容。
要回答您的特定问题:
Java要求这样做,但是Java字节码没有这种限制。据推测,科特林对参数不应该为null的了解导致编译器插入代码以在运行时进行检查。由于字节码自由地允许在超级构造函数调用之前进行代码(带有一些有关访问未初始化对象的警告),因此在尝试对其进行反编译之前没有问题。
这看起来像是Kotlin的特定功能,所以不确定。
有些注释保留在字节码中,有些则没有。 @Override
没有运行时行为,仅用作编译时检查,因此将其设置为仅编译时是有意义的。
在字节码级别,没有布尔值(方法签名除外)。所有布尔(以及char和short和byte以及布尔型)局部变量都被编译为ints,false = 0,true =1。这意味着反编译器必须猜测给定的变量是int还是boolean,即一项非常艰巨的任务,不可能永远做到正确。
可能是反编译器感到困惑,或者字节码太难将其反编译为有效的Java。请记住,Java字节码的类型检查要比Java宽松得多,并且编译后许多编译时间信息会消失,因此将字节码反编译为有效的Java并非易事。
因为没有编写反编译器来简化程序?您可以尝试让反编译器作者添加该代码,但是比您想象的要难得多。
如果不查看字节码就无法确定,但是Throwable强制转换很可能是反编译器添加的。请记住,字节码和Java源代码是不兼容的格式,反编译不是精确的转换。
是真的在生产中执行的代码还是Java Decompiler不能弄清所有这些东西?
如果您对此主题感兴趣,我强烈建议您学习Java字节码的工作原理,然后使用a Java bytecode disassembler来查看引擎盖下的实际情况。这将使您看到字节码中的内容以及反编译的内容。
有两种行之有效的方法来找出字节码的作用:运行并读取它。
如果您运行它,您将看到一切都按Kotlin编写的方式工作。
现在让我阅读字节码并进行解释。
1。字节码不在乎Java要求。即使在Java中,也可以在super()
调用之前执行某些操作。例如,在此之前执行super(string + string);
加法。
2。字节码:
GETSTATIC me/stackoverflow/a10/PagingOnScrollListener$1.INSTANCE : Lme/stackoverflow/a10/PagingOnScrollListener$1;
CHECKCAST kotlin/jvm/functions/Function2
INVOKESPECIAL me/stackoverflow/a10/PagingOnScrollListener.<init> (Lkotlin/jvm/functions/Function2;)V
我在Java中的翻译:
this((Function2)PagingOnScrollListener$1.INSTANCE);
我认为Java反编译器由于类名称1
怪异而无法正确反编译。 Java使用数字作为匿名类名,但是这些类不能具有静态声明。
这里没有创建新的函数实例,因为Kotlin非常聪明,可以看到每次都可以使用相同的实例。
3。 @Override
注释用@Retention(RetentionPolicy.SOURCE)
注释,因此已从字节码中删除。
4。字节码:
ICONST_0
ISTORE 7
我在Java中的翻译:
int i7 = 0;
由于Java字节码中没有boolean
局部变量,它们被int
变量替换,因此Java反编译器无法正确地对其进行反编译。
5。 Kotlin在此处创建的字节码非常复杂。 Java反编译器必须像我一样无法正确反编译。
6。反编译器当前不支持这种简化。
7。使用字节码进行此强制转换:
CHECKCAST java/lang/Throwable