- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我发现了一些类型推断的非直觉行为。因此,语义等效代码的工作方式不同,具体取决于编译器推断出的有关函数返回类型的信息。当您在最小单元测试中重现此案例时,或多或少会清楚发生了什么。但我担心在编写框架代码时,这种行为可能很危险。
下面的代码说明了问题,我的问题是:
为什么 puzzler1
来自 notok1
的电话无条件抛出NPE?据我从字节码了解,ACONST_NULL ATHROW
在 puzzler1
之后立即抛出 NPE调用,忽略返回值。
编译器推断类型时忽略上限(<T : TestData>
)是否正常?
添加suspend
NPE变成ClassCastException是不是bug函数修饰符?当然,我明白runBlocking+suspend
call 给了我们不同的字节码,但“协程化”代码不应该尽可能等同于常规代码吗?
有没有办法重写puzzler1
以某种方式编码,消除不清晰?
@Suppress("UnnecessaryVariable", "MemberVisibilityCanBePrivate", "UNCHECKED_CAST", "RedundantSuspendModifier")
class PuzzlerTest {
open class TestData(val value: String)
lateinit var whiteboxResult: TestData
fun <T : TestData> puzzler1(
resultWrapper: (String) -> T
): T {
val result = try {
resultWrapper("hello")
} catch (t: Throwable) {
TestData(t.message!!) as T
}
whiteboxResult = result
return result // will always return TestData type
}
// When the type of `puzzler1` is inferred to TestData, the code works as expected:
@Test
fun ok() {
val a = puzzler1 { TestData("$it world") }
// the same result inside `puzzler1` and outside of it:
assertEquals("hello world", whiteboxResult.value)
assertEquals("hello world", a.value)
}
// But when the type of `puzzler1` is not inferred to TestData, the result is rather unexpected.
// And compiler ignores the upper bound <T : TestData>:
@Test
fun notok1() {
val a = try {
puzzler1 { throw RuntimeException("goodbye") }
} catch (t: Throwable) {
t
}
assertEquals("goodbye", whiteboxResult.value)
assertTrue(a is NullPointerException) // this is strange
}
// The same code as above, but with enough information for the compiler to infer the type:
@Test
fun notok2() {
val a = puzzler1 {
@Suppress("ConstantConditionIf")
if (true)
throw RuntimeException("goodbye")
else {
// the type is inferred from here
TestData("unreachable")
// The same result if we write:
// puzzler1<TestData> { throw RuntimeException("goodbye") }
}
}
assertEquals("goodbye", whiteboxResult.value)
assertEquals("goodbye", (a as? TestData)?.value) // this is stranger
}
// Now create the `puzzler2` which only difference from `puzzler1` is `suspend` modifier:
suspend fun <T : TestData> puzzler2(
resultWrapper: (String) -> T
): T {
val result = try {
resultWrapper("hello")
} catch (t: Throwable) {
TestData(t.message!!) as T
}
whiteboxResult = result
return result
}
// Do exactly the same test as `notok1` and NullPointerException magically becomes ClassCastException:
@Test
fun notok3() = runBlocking {
val a = try {
puzzler2 { throw RuntimeException("goodbye") }
} catch (t: Throwable) {
t
}
assertEquals("goodbye", whiteboxResult.value)
assertTrue(a is ClassCastException) // change to coroutines and NullPointerException becomes ClassCastException
}
// The "fix" is the same as `notok2` by providing the compiler with info to infer `puzzler2` return type:
@Test
fun notok4() = runBlocking {
val a = try {
puzzler2<TestData> { throw RuntimeException("goodbye") }
// The same result if we write:
// puzzler2 {
// @Suppress("ConstantConditionIf")
// if (true)
// throw RuntimeException("goodbye")
// else
// TestData("unreachable")
// }
} catch (t: Throwable) {
t
}
assertEquals("goodbye", whiteboxResult.value)
assertEquals("goodbye", (a as? TestData)?.value)
}
}
最佳答案
throw RuntimeException("goodbye")
的类型是什么? ?好吧,因为它从不返回值,你可以在任何你喜欢的地方使用它,无论期望什么类型的对象,它总是会进行类型检查。我们说它的类型是 Nothing
.这种类型没有值,它是所有类型的子类型。因此,在 notok1
, 你有一个调用 puzzler1<Nothing>
.来自构建的 Actor TestData
至 T = Nothing
里面puzzler1<Nothing>
不健全但未经检查,puzzler1
当它的类型签名说它不应该能够时最终返回。 notok1
注意到 puzzler1
当它说它不能时已经返回,并立即抛出异常。它的描述性不强,但我相信它抛出 NPE 的原因是因为如果无法返回的函数已经返回,则出现了“严重错误”,因此语言决定程序应该尽快结束。
对于 notok2
,你实际上得到了 T = TestData
:if
的一个分支返回 Nothing
, 其他 TestData
,其中的 LUB 是 TestData
(因为 Nothing
是 TestData
的子类型)。 notok2
没有理由相信puzzler1<TestData>
无法返回,所以它不会设置陷阱尽快死亡 puzzler1
返回。
notok3
与 notok1
本质上有相同的问题.返回类型,Nothing
, 意味着唯一的 puzzler2<Nothing>
会做的是抛出异常。 notok3
中的协程处理代码因此期望协程持有一个 Throwable
并包含重新抛出它的代码,但不包含处理实际返回值的代码。当puzzler2
实际上确实返回,notok3
试图施放 TestData
进入 Throwable
并失败了。 notok4
出于同样的原因工作 notok2
这个困惑的解决方案就是不使用不合理的转换。 有时 puzzler1<T>
/puzzler2<T>
将能够返回 T
,如果传递的函数实际上返回一个 T
.但是,如果该函数抛出,它们只能返回 TestData
。 , 和一个 TestData
不是 T
(一个 T
是一个 TestData
,而不是相反)。 puzzler1
的正确签名(对于 puzzler2
也是如此)是
fun <T : TestData> puzzler1(resultWrapper: (String) -> T): TestData
因为函数在返回类型上是协变的,所以你可以去掉类型参数
fun puzzler1(resultWrapper: (String) -> TestData): TestData
关于非直觉类型推断的 Kotlin 案例,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60760071/
当使用模板模板参数时,我如何推断或删除模板模板的模板类型? 考虑以下 SSCCE: #include #include #include using namespace std; templat
假设我有一些特质: trait A[T] { def foo: T } 一个扩展它的类: class B[T](t: T) extends A[T] { def foo = t } 以及父特征的子特征
一边玩-rectypes在某些时候选择 OCaml 我只是迷路了。 这个表达式几乎可以打字: # fun x -> x x;; - : ('a -> 'b as 'a) -> 'b = 但是这里 O
我正在编写一个类似 CRUD 的应用程序,并且通过主键进行大量查找(主键可以有不同的类型)。所以我定义了以下类型类: {-# LANGUAGE MultiParamTypeClasses #-} cl
我已经创建了关系 A 'is functional parent of' B并定义 'has functional parent'作为 'is functional parent of' 的倒数. '
给定一个使用 Kotlin 版本 1.3.61 和 JOOQ 版本 3.13.1 的系统,这样的方法会构建 union正常查询: val selectCommonPart = coalesce
考虑以下错误代码: fun x = if (null x) then 0 else (take 50 x) : (fun (drop 50 x)) 我注意到,我可以毫无问题地将它加载到
给定一个具有以下类型的函数 a: a::x -> Bool 和以下类型的另一个函数 b: b::Bool -> y 我正在尝试找出推断以下函数类型的步骤: c =\d -> d a b 有人可以帮助解
我正在尝试使用 Infer 工具来分析我的应用代码。我关注了these steps每次我尝试运行 infer -- gradle build 时,我都会收到以下错误: infer -- gradle
所以我制作了这个模板来定义内联仿函数: template struct AsFunctor { template std::invoke_result_t operator()(A
是否可以推断 CRTP 基类中模板化成员函数的返回类型? 虽然推断参数类型效果很好,但它因返回类型而失败。考虑以下示例。 #include template struct base { tem
使用 Series.interpolate 很容易在 Pandas.DataFrame 中插入值,如何进行外推? 例如,给定一个如图所示的 DataFrame,我们如何将它外推 14 个月到 2014
我想知道为什么这不起作用(缺少参数类型)? Seq(1,2,3).toSet.map(_ + 1) 但这确实: val foo = Seq(1,2,3).toSet foo.map(_ + 1)
我没有必要使用 SQLite3 shell 工具来维护一个小型数据库。我正在使用 -header -ascii标志,尽管据我所知,这适用于任何输出选择。我正在寻找一种方法来避免对返回的任何一个值的类型
我有以下组件 type PropTypes = { items: T[], header: (item: T) => React.Element, body: (item: T) => R
我想在 Eclipse/JSDT 中指定实例变量的类型,如下例所示: /** * @constructor */ function A() { /** @type Node */
我正在用 Python 编写一个方法,它看起来像这样: def rgb_to_grayscale(image): print(image.shape) pass 此处预期的类型是 nu
我有一个 my_values 数组,我正在尝试为其推断 true_values 数组中最接近、较小的值。使用下面的 find_nearest 函数并不能完成我想要的。我如何追加它以找到最近的、较小的值
在下面的代码中: template int b(int q, const std::array& types) { int r = q; for (int t : types)
在 Pandas DataFrame 中插入 NaN 单元非常容易: In [98]: df Out[98]: neg neu pos av
我是一名优秀的程序员,十分优秀!