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

案例类复制’方法’与超类

发布时间:2020-12-16 09:33:50 所属栏目:安全 来源:网络整理
导读:我想做这样的事情: sealed abstract class Base(val myparam:String)case class Foo(override val myparam:String) extends Base(myparam)case class Bar(override val myparam:String) extends Base(myparam)def getIt( a:Base ) = a.copy(myparam="change
我想做这样的事情:

sealed abstract class Base(val myparam:String)

case class Foo(override val myparam:String) extends Base(myparam)
case class Bar(override val myparam:String) extends Base(myparam)

def getIt( a:Base ) = a.copy(myparam="changed")

我不能,因为在getIt的上下文中,我没有告诉编译器每个Base都有一个’copy’方法,但是copy不是一个方法,所以我不认为有一个特征或抽象方法我可以放在基地让这个工作正常。还是呢?

如果我尝试将Base定义为抽象类Base {def copy(myparam:String):Base},则case类Foo(myparam:String)扩展类Foo中的基本结果需要是抽象的,因为类Base中的方法复制(myparam:String)未定义基数

有没有其他的方法来告诉编译器,所有的Base类都将是它们的实现中的case类?某些特征意味着“具有案例类的属性”?

我可以使Base成为一个case类,但是我收到编译器警告,表示不支持case类继承?

我知道我也可以:

def getIt(f:Base)={ 
  (f.getClass.getConstructors.head).newInstance("yeah").asInstanceOf[Base]
}

但…似乎很丑陋。

思考?我的整个做法是“错”吗?

UPDATE我更改了基类以包含该属性,并使case类使用“override”关键字。这更好地反映了实际的问题,并使问题更加现实,考虑到Edmondo1984的回应。

解决方法

这是旧的答案,在问题改变之前。

强大的类型编程语言阻止您尝试做的事情。我们来看看为什么

具有以下签名的方法的想法:

def getIt( a:Base ) : Unit

这个方法的主体是否可以访问通过Base类或接口可见的属性,即仅在Base类/接口或其父类上定义的属性和方法。在代码执行期间,传递给getIt方法的每个特定实例可能具有不同的子类,但是编译类型将始终为Base

人们可以这样推理:

Ok I have a class Base,I inherit it in two case classes and I add a
property with the same name,and then I try to access the property on
the instance of Base.

一个简单的例子显示为什么这是不安全的:

sealed abstract class Base
case class Foo(myparam:String) extends Base
case class Bar(myparam:String) extends Base
case class Evil(myEvilParam:String) extends Base

def getIt( a:Base ) = a.copy(myparam="changed")

在以下情况下,如果编译器在编译时没有发生错误,则意味着代码将尝试访问在运行时不存在的属性。这在严格类型的编程语言中是不可能的:您已经对编写的代码进行了交易限制,以便编译器对代码进行更强大的验证,知道这会大大减少代码可能包含的错误数量

这是新的答案。这是一个很长的时间,因为在得出结论之前需要几点

不幸的是,你不能依靠案例类复制的机制来实现你的建议。复制方法的工作方式只是一个复制构造函数,您可以在非案例类中实现自己。我们来创建一个case类并在REPL中进行反汇编:

scala>  case class MyClass(name:String,surname:String,myJob:String)
defined class MyClass

scala>  :javap MyClass
Compiled from "<console>"
public class MyClass extends java.lang.Object implements scala.ScalaObject,scala.Product,scala.Serializable{
    public scala.collection.Iterator productIterator();
    public scala.collection.Iterator productElements();
    public java.lang.String name();
    public java.lang.String surname();
    public java.lang.String myJob();
    public MyClass copy(java.lang.String,java.lang.String,java.lang.String);
    public java.lang.String copy$default$3();
    public java.lang.String copy$default$2();
    public java.lang.String copy$default$1();
    public int hashCode();
    public java.lang.String toString();
    public boolean equals(java.lang.Object);
    public java.lang.String productPrefix();
    public int productArity();
    public java.lang.Object productElement(int);
    public boolean canEqual(java.lang.Object);
    public MyClass(java.lang.String,java.lang.String);
}

在Scala中,复制方法采用三个参数,最终可以使用当前实例中没有指定的实例(Scala语言在其功能中提供方法调用中参数的默认值)

我们在分析中进行下一步,并将代码重新更新:

sealed abstract class Base(val myparam:String)

case class Foo(override val myparam:String) extends Base(myparam)
case class Bar(override val myparam:String) extends Base(myparam)

def getIt( a:Base ) = a.copy(myparam="changed")

现在为了做这个编译,我们需要在getIt(a:MyType)的签名中使用一个遵循以下合同的MyType:

Anything that has a parameter myparam and maybe other parameters which
have default value

所有这些方法都是适合的:

def copy(myParam:String) = null
  def copy(myParam:String,myParam2:String="hello") = null
  def copy(myParam:String,myParam2:Option[Option[Option[Double]]]=None) = null

没有办法在Scala中表达这份合同,但是有一些可以帮助的高级技术。

我们可以做的第一个观察是,Scala中的案例类和元组之间存在严格的关系。事实上,case类是以某种方式与额外的行为和命名属性的元组。

第二个观察结果是,由于您的类层次结构的属性数量不能保证是相同的,所以复制方法签名不能保证是相同的。

实际上,假设AnyTuple [Int]描述了第一个值为Int类型的任何大小的元组,我们正在寻找类似的东西:

def copyTupleChangingFirstElement(myParam:AnyTuple[Int],newValue:Int) = myParam.copy(_1=newValue)

如果所有的元素都是Int,这不会是困难的。与所有相同类型的元素的元组是List,我们知道如何替换List的第一个元素。我们需要将任何TupleX转换为List,替换第一个元素,并将List转换回TupleX。是的,我们将需要为X可能承担的所有值写入所有转换器。烦人但不难

在我们的情况下,并不是所有的元素都是Int。我们想要对元组进行处理,如果第一个元素是一个Int,它们都是一样的。这就是所谓的

“Abstracting over arity”

即以一般的方式处理不同大小的元组,与其大小无关。要做到这一点,我们需要将它们转换为支持异质类型的特殊列表,名为HList

结论

案例类继承因为非常好的原因而被弃用,您可以从邮件列表中的多个帖子中找到:http://www.scala-lang.org/node/3289

你有两个策略来处理你的问题:

>如果您需要更改数量有限的字段,请使用方法,例如由@Ron建议的方法,该方法具有复制方法。如果你想在没有丢失类型信息的情况下执行它,我将去基础类的生成

sealed abstract class Base[T](val param:String){
  def copy(param:String):T
}

class Foo(param:String) extends Base[Foo](param){
  def copy(param: String) = new Foo(param)
}

def getIt[T](a:Base[T]) : T = a.copy("hello")

scala>  new Foo("Pippo")
res0: Foo = Foo@4ab8fba5

scala>  getIt(res0)
res1: Foo = Foo@5b927504

scala>  res1.param
res2: String = hello

>如果你真的想抽象出来,一个解决方案是使用一个名为Shapeless的Miles Sabin开发的图书馆。这里有一个问题,经过讨论后被问到:Are HLists nothing more than a convoluted way of writing tuples?但我告诉你,这将给你一些头痛

(编辑:李大同)

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

    推荐文章
      热点阅读