Empirical testing shows you can add the virtual class modifier to an already packaged global class with no side effects to the packaging process. What you can't do is then add a new method that isn't also marked as virtual.
Initial class:
global class GlobalTestClass {
global string echo(string input) {
return input + ' response';
}
}
And its associated test case:
@IsTest
public class GlobalTestClass_Test {
@IsTest
public static void echoTest() {
GlobalTestClass gtc = new GlobalTestClass();
string response = gtc.echo('hello');
System.assertEquals('hello response', response);
}
}
Created v1 of the managed package - https://login.salesforce.com/packaging/installPackage.apexp?p0=04t280000011wZi
Updated global class -
global virtual class GlobalTestClass {
global string echo(string input) {
return input + ' response';
}
}
Created v1.1 of the managed package -
https://login.salesforce.com/packaging/installPackage.apexp?p0=04t280000011wZn
Updated global class again. Now with a virtual method as well:
global virtual class GlobalTestClass {
global virtual string echo(string input) {
return input + ' response';
}
}
Create v1.2 of the managed package - https://login.salesforce.com/packaging/installPackage.apexp?p0=04t280000011wZx
What I can't do - add a new non-virtual method to the global class. The following fails:
global virtual class GlobalTestClass {
global virtual string echo(string input) {
return input + ' response';
}
global string echo2(string input2) {
return input2;
}
}
Cannot add new final methods to virtual or abstract classes: String namespace.GlobalTestClass.echo2(String)