By UVM convention, as well as in many other methodologies, clone() is virtual, copy() is not. It would be very difficult to define copy as virtual. And even if you did, you would need to define another virtual method that constructs the object to become the target of the copy. And that object’s handle needs to be stored in another variable.
By making clone virtual, it is very easy to construct the proper derived object, and then call the derived copy method. Since the clone method returns the newly constructed and copied object, it is also easier to use the clone method as an argument to another method, reducing the chances of leaving stray object handles around that inhibit the automatic memory management.