加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 综合聚焦 > 服务器 > 安全 > 正文

最好的Scala模仿Groovy的安全解除引用运算符(?。)?

发布时间:2020-12-16 09:29:24 所属栏目:安全 来源:网络整理
导读:我想知道什么是最好的Scala模仿Groovy的 safe-dereference operator (?.),或者至少有一些接近的选择? 我在Daniel Spiewak的博客上发表了discussed it breifly,但是想把它打开到StackOverFlow …… 为了每个人的时间,这是丹尼尔的最初回应,我的反击,以
我想知道什么是最好的Scala模仿Groovy的 safe-dereference operator (?.),或者至少有一些接近的选择?

我在Daniel Spiewak的博客上发表了discussed it breifly,但是想把它打开到StackOverFlow ……

为了每个人的时间,这是丹尼尔的最初回应,我的反击,以及他的第二个回应:

@Antony

Actually,I looked at doing that one
first. Or rather,I was trying to
replicate Ragenwald’s andand
“operator” from Ruby land. The problem
is,this is a bit difficult to do
without proxies. Consider the
following expression (using Ruby’s
andand,but it’s the same with
Groovy’s operator):

test.andand().doSomething()

I could create an implicit conversion
from Any => some type implementing the
andand() method,but that’s where the
magic stops. Regardless of whether the
value is null or not,the
doSomething() method will still
execute. Since it has to execute on
some target in a type-safe manner,
that would require the implementation
of a bytecode proxy,which would be
flaky and weird (problems with
annotations,final methods,
constructors,etc).

A better alternative is to go back to
the source of inspiration for both
andand as well as Groovy’s safe
dereference operator: the monadic map
operation. The following is some Scala
syntax which uses Option to implement
the pattern:

val something: Option[String] = … //
presumably could be either Some(…) or
None

val length = something.map(_.length)

After this,length either be
Some(str.length) (where str is the
String object contained within the
Option),or None. This is exactly how
the safe-dereferencing operator works,
except it uses null rather than a
type-safe monad.

As pointed out above,we could define
an implicit conversion from some type
T => Option[T] and then map in that
fashion,but some types already have
map defined,so it wouldn’t be very
useful. Alternatively,I could
implement something similar to map but
with a separate name,but any way it
is implemented,it will rely upon a
higher-order function rather than a
simple chained call. It seems to be
just the nature of statically typed
languages (if anyone has a way around
this,feel free to correct me).

Daniel Spiewak Monday,July 7,2008 at
1:42 pm

我的第二个问题:

Thanks for the response Daniel
regarding ?. I think I missed it! I
think I understand what you’re
proposing,but what about something
like this,assuming you don’t have
control over the sources:

company?.getContactPerson?.getContactDetails?.getAddress?.getCity

Say it’s a java bean and you can’t go
in and change the return values to
Something[T] – what can we do there?

Antony Stubbs Tuesday,July 21,2009
at 8:07 pm oh gosh – ok on re-read
that’s where you’re proposing the
implicit conversion from T to
Option[T] right? But would you still
be able to chain it together like
that? You’d still need the map right?
hmm….

var city = company.map(_.getContactPerson.map(_.getContactDetails.map(_.getAddress.map(_.getCity))))

?

Antony Stubbs Tuesday,2009
at 8:10 pm

他的第二回应:

@Antony

We can’t really do much of anything in
the case of company?.getContactPerson,
etc… Even assuming this were valid
Scala syntax,we would still need some
way to prevent the later calls in the
chain. This is not possible if we’re
not using function values. Thus,
something like map is really the only
option.

An implicit conversion to Option
wouldn’t be bad,but by making things
implicit,we’re circumventing some of
the protection of the type system. The
best way to do this sort of thing is
to use for-comprehensions in concert
with Option. We can do map and
flatMap,but it’s much nicer with
magical syntax:

for {
   c < - company
   person <- c.getContactPerson   
   details <- person.getContactDetails
   address <- details.getAddress 
  } yield address.getCity

Daniel Spiewak Tuesday,2009 at 9:28 pm

附:如果Daniel将他原来的答案作为答案发布在他的博客上,我会编辑问题以便为系统删除它们。

解决方法

这个怎么样?

def ?[A](block: => A) =
  try { block } catch {
    case e: NullPointerException if e.getStackTrace()(2).getMethodName == "$qmark" => null
    case e => throw e
  }

使用这个小片段,您可以安全地取消引用,代码本身非常简洁:

val a = ?(b.c.d.e)

a ==如果b或b.c或b.c.d或b.c.d.e为null,则为null,否则为a == b.c.d.e

我认为当你使用像Scala这样的语言时,安全解除引用运算符的价值会降低,这种语言具有逐个调用和隐含的功能。

ps:根据以下评论之一修改上面的代码以处理NullPointerException时的情况
实际上抛出了被调用的函数。

顺便说一句,我认为使用下面的函数是一种更加惯用的Scala编写方式:

def ??[A](block: => A): Option[A] = ?(block) match {
    case a: A => Some(a)
    case _ => None
  }

像这样:

??(a.b.c.d) match {
    case Some(result) => // do more things with result
    case None => // handle "null" case
  }

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读